From f95744170f008e5aecf912a4a380e6191296089b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:06:26 +0300 Subject: [PATCH 001/489] Add skin config lookup for combo colours --- osu.Game/Skinning/SkinComboColourLookup.cs | 26 +++++++++++++++++++++ osu.Game/Skinning/SkinProvidingContainer.cs | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Skinning/SkinComboColourLookup.cs diff --git a/osu.Game/Skinning/SkinComboColourLookup.cs b/osu.Game/Skinning/SkinComboColourLookup.cs new file mode 100644 index 0000000000..33e35a96fb --- /dev/null +++ b/osu.Game/Skinning/SkinComboColourLookup.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Skinning +{ + public class SkinComboColourLookup + { + /// + /// The index to use for deciding the combo colour. + /// + public readonly int ColourIndex; + + /// + /// The combo information requesting the colour. + /// + public readonly IHasComboInformation Combo; + + public SkinComboColourLookup(int colourIndex, IHasComboInformation combo) + { + ColourIndex = colourIndex; + Combo = combo; + } + } +} diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index cf22b2e820..a7fc1306c9 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -72,7 +72,7 @@ namespace osu.Game.Skinning { if (skin != null) { - if (lookup is GlobalSkinColours || lookup is SkinCustomColourLookup) + if (lookup is GlobalSkinColours || lookup is SkinComboColourLookup || lookup is SkinCustomColourLookup) return lookupWithFallback(lookup, AllowColourLookup); return lookupWithFallback(lookup, AllowConfigurationLookup); From 9be8d3f0d21b77a63110ea0335de65a3ce0dfc06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:12:17 +0300 Subject: [PATCH 002/489] Add overridable combo colour retrieval method and handle in legacy skin --- osu.Game/Skinning/LegacySkin.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index eae3b69233..e827dca232 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -16,6 +16,7 @@ using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.IO; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; using osuTK.Graphics; @@ -119,6 +120,9 @@ namespace osu.Game.Skinning break; + case SkinComboColourLookup comboColour: + return SkinUtils.As(GetComboColour(Configuration, comboColour.ColourIndex, comboColour.Combo)); + case SkinCustomColourLookup customColour: return SkinUtils.As(getCustomColour(Configuration, customColour.Lookup.ToString())); @@ -276,6 +280,18 @@ namespace osu.Game.Skinning return null; } + /// + /// Retrieves the correct combo colour for a given colour index and information on the combo. + /// + /// The source to retrieve the combo colours from. + /// The preferred index for retrieving the combo colour with. + /// Information on the combo whose using the returned colour. + protected virtual IBindable GetComboColour(IHasComboColours source, int colourIndex, IHasComboInformation combo) + { + var colour = source.ComboColours?[colourIndex % source.ComboColours.Count]; + return colour.HasValue ? new Bindable(colour.Value) : null; + } + private IBindable getCustomColour(IHasCustomColours source, string lookup) => source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable(col) : null; From 78794935b47f95648ac620378ddfe11108933e59 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:11:45 +0300 Subject: [PATCH 003/489] Handle combo colour lookups in other skins --- .../Gameplay/TestSceneHitObjectAccentColour.cs | 10 ++-------- osu.Game/Skinning/DefaultSkin.cs | 11 +++++++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 883791c35c..d08d08390b 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -127,14 +127,8 @@ namespace osu.Game.Tests.Gameplay { switch (lookup) { - case GlobalSkinColours global: - switch (global) - { - case GlobalSkinColours.ComboColours: - return SkinUtils.As(new Bindable>(ComboColours)); - } - - break; + case SkinComboColourLookup comboColour: + return SkinUtils.As(new Bindable(ComboColours[comboColour.ColourIndex % ComboColours.Count])); } throw new NotImplementedException(); diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 0b3f5f3cde..a2d05a918c 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osu.Game.Beatmaps.Formats; using osuTK.Graphics; namespace osu.Game.Skinning @@ -28,10 +29,10 @@ namespace osu.Game.Skinning public override IBindable GetConfig(TLookup lookup) { + // todo: this code is pulled from LegacySkin and should not exist. + // will likely change based on how databased storage of skin configuration goes. switch (lookup) { - // todo: this code is pulled from LegacySkin and should not exist. - // will likely change based on how databased storage of skin configuration goes. case GlobalSkinColours global: switch (global) { @@ -40,9 +41,15 @@ namespace osu.Game.Skinning } break; + + case SkinComboColourLookup comboColour: + return SkinUtils.As(new Bindable(getComboColour(Configuration, comboColour.ColourIndex))); } return null; } + + private static Color4 getComboColour(IHasComboColours source, int colourIndex) + => source.ComboColours[colourIndex % source.ComboColours.Count]; } } From eeeb001d62279df9a5430b4345844dccc042acfd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:17:27 +0300 Subject: [PATCH 004/489] Refactor combo colour retrieval logic to request skin lookups instead --- osu.Game.Rulesets.Catch/Objects/Banana.cs | 3 ++- .../Objects/PalpableCatchHitObject.cs | 4 ++-- .../Objects/Drawables/DrawableHitObject.cs | 3 +-- .../Objects/Types/IHasComboInformation.cs | 23 +++++++++++++------ .../Timeline/TimelineHitObjectBlueprint.cs | 4 +--- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs index 178306b3bc..e5a36d08db 100644 --- a/osu.Game.Rulesets.Catch/Objects/Banana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs @@ -9,6 +9,7 @@ using osu.Game.Audio; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; using osu.Game.Utils; using osuTK.Graphics; @@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Objects } // override any external colour changes with banananana - Color4 IHasComboInformation.GetComboColour(IReadOnlyList comboColours) => getBananaColour(); + Color4 IHasComboInformation.GetComboColour(ISkin skin) => getBananaColour(); private Color4 getBananaColour() { diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs index 0cd3af01df..24c12343e5 100644 --- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects @@ -43,6 +43,6 @@ namespace osu.Game.Rulesets.Catch.Objects } } - Color4 IHasComboInformation.GetComboColour(IReadOnlyList comboColours) => comboColours[(IndexInBeatmap + 1) % comboColours.Count]; + Color4 IHasComboInformation.GetComboColour(ISkin skin) => IHasComboInformation.GetSkinComboColour(this, skin, IndexInBeatmap + 1); } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 86c733c392..63ae2e286f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -509,8 +509,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { if (!(HitObject is IHasComboInformation combo)) return; - var comboColours = CurrentSkin.GetConfig>(GlobalSkinColours.ComboColours)?.Value ?? Array.Empty(); - AccentColour.Value = combo.GetComboColour(comboColours); + AccentColour.Value = combo.GetComboColour(CurrentSkin); } /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 4f66802079..03e6f76cca 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -1,9 +1,8 @@ // 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 JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Types @@ -40,11 +39,21 @@ namespace osu.Game.Rulesets.Objects.Types bool LastInCombo { get; set; } /// - /// Retrieves the colour of the combo described by this object from a set of possible combo colours. - /// Defaults to using to decide the colour. + /// Retrieves the colour of the combo described by this object. /// - /// A list of possible combo colours provided by the beatmap or skin. - /// The colour of the combo described by this object. - Color4 GetComboColour([NotNull] IReadOnlyList comboColours) => comboColours.Count > 0 ? comboColours[ComboIndex % comboColours.Count] : Color4.White; + /// The skin to retrieve the combo colour from, if wanted. + Color4 GetComboColour(ISkin skin) => GetSkinComboColour(this, skin, ComboIndex); + + /// + /// Retrieves the colour of the combo described by a given object from a given skin. + /// + /// The combo information, should be this. + /// The skin to retrieve the combo colour from. + /// The index to retrieve the combo colour with. + /// + protected static Color4 GetSkinComboColour(IHasComboInformation combo, ISkin skin, int comboIndex) + { + return skin.GetConfig(new SkinComboColourLookup(comboIndex, combo))?.Value ?? Color4.White; + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index dbe689be2f..67a28c4fa0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -139,8 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (!(Item is IHasComboInformation combo)) return; - var comboColours = skin.GetConfig>(GlobalSkinColours.ComboColours)?.Value ?? Array.Empty(); - var comboColour = combo.GetComboColour(comboColours); + var comboColour = combo.GetComboColour(skin); if (IsSelected) { From cd6d070b4aacc33a880562c928001fdc90dee920 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:27:57 +0300 Subject: [PATCH 005/489] Consider "combo offsets" as legacy logic and separate from combo information --- .../Beatmaps/CatchBeatmapConverter.cs | 3 --- .../Objects/CatchHitObject.cs | 2 -- .../Beatmaps/OsuBeatmapConverter.cs | 6 ++--- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 14 ++++------- .../TestSceneHitObjectAccentColour.cs | 1 - osu.Game/Beatmaps/BeatmapProcessor.cs | 12 +++++++++- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 5 ++-- .../Objects/Legacy/Catch/ConvertHit.cs | 2 -- .../Legacy/Catch/ConvertHitObjectParser.cs | 14 ----------- .../Objects/Legacy/Catch/ConvertSlider.cs | 2 -- .../Objects/Legacy/Catch/ConvertSpinner.cs | 2 -- .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 6 +++-- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 ++-- .../Objects/Legacy/Osu/ConvertSlider.cs | 6 +++-- .../Objects/Legacy/Osu/ConvertSpinner.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasCombo.cs | 5 ---- .../Types/IHasLegacyBeatmapComboOffset.cs | 24 +++++++++++++++++++ 17 files changed, 56 insertions(+), 54 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 34964fc4ae..8b0213bfeb 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps RepeatCount = curveData.RepeatCount, X = positionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 }.Yield(); @@ -49,7 +48,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, Duration = endTime.Duration, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, }.Yield(); default: @@ -58,7 +56,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps StartTime = obj.StartTime, Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, X = positionData?.X ?? 0 }.Yield(); } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index ae45182960..ce2da314ca 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -67,8 +67,6 @@ namespace osu.Game.Rulesets.Catch.Objects public virtual bool NewCombo { get; set; } - public int ComboOffset { get; set; } - public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public int IndexInCurrentCombo diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index a2fc4848af..d812f86938 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps RepeatCount = curveData.RepeatCount, Position = positionData?.Position ?? Vector2.Zero, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, + LegacyBeatmapComboOffset = (original as IHasLegacyBeatmapComboOffset)?.LegacyBeatmapComboOffset ?? 0, LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset, // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. // this results in more (or less) ticks being generated in /// The radius of hit objects (ie. the radius of a ). @@ -73,14 +73,6 @@ namespace osu.Game.Rulesets.Osu.Objects public virtual bool NewCombo { get; set; } - public readonly Bindable ComboOffsetBindable = new Bindable(); - - public int ComboOffset - { - get => ComboOffsetBindable.Value; - set => ComboOffsetBindable.Value = value; - } - public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public virtual int IndexInCurrentCombo @@ -105,6 +97,10 @@ namespace osu.Game.Rulesets.Osu.Objects set => LastInComboBindable.Value = value; } + public int LegacyBeatmapComboOffset { get; set; } + + public int LegacyBeatmapComboIndex { get; set; } + protected OsuHitObject() { StackHeightBindable.BindValueChanged(height => diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index d08d08390b..99a681acf8 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -82,7 +82,6 @@ namespace osu.Game.Tests.Gameplay private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation { public bool NewCombo { get; set; } - public int ComboOffset => 0; public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index b7b5adc52e..ac2d7d0fc1 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps if (obj.NewCombo) { obj.IndexInCurrentCombo = 0; - obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + obj.ComboOffset + 1; + obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + 1; if (lastObj != null) lastObj.LastInCombo = true; @@ -48,6 +48,16 @@ namespace osu.Game.Beatmaps obj.ComboIndex = lastObj.ComboIndex; } + if (obj is IHasLegacyBeatmapComboOffset legacyObj) + { + var lastLegacyObj = (IHasLegacyBeatmapComboOffset)lastObj; + + if (obj.NewCombo) + legacyObj.LegacyBeatmapComboIndex = (lastLegacyObj?.LegacyBeatmapComboIndex ?? 0) + legacyObj.LegacyBeatmapComboOffset + 1; + else if (lastLegacyObj != null) + legacyObj.LegacyBeatmapComboIndex = lastLegacyObj.LegacyBeatmapComboIndex; + } + lastObj = obj; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index acbf57d25f..e494f6c95c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -292,10 +292,11 @@ namespace osu.Game.Beatmaps.Formats if (hitObject is IHasCombo combo) { - type = (LegacyHitObjectType)(combo.ComboOffset << 4); - if (combo.NewCombo) type |= LegacyHitObjectType.NewCombo; + + if (hitObject is IHasLegacyBeatmapComboOffset comboOffset) + type |= (LegacyHitObjectType)(comboOffset.LegacyBeatmapComboOffset << 4); } switch (hitObject) diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 19722fb796..f1a03a03e2 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -13,7 +13,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X { get; set; } public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index c10c8dc30f..d71ddfd05e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -18,21 +18,16 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch } private bool forceNewCombo; - private int extraComboOffset; protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset) { newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - forceNewCombo = false; - extraComboOffset = 0; return new ConvertHit { X = position.X, NewCombo = newCombo, - ComboOffset = comboOffset }; } @@ -40,16 +35,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch List> nodeSamples) { newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - forceNewCombo = false; - extraComboOffset = 0; return new ConvertSlider { X = position.X, NewCombo = FirstObject || newCombo, - ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount @@ -58,11 +49,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { - // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo - // Their combo offset is still added to that next hitobject's combo index - forceNewCombo |= FormatVersion <= 8 || newCombo; - extraComboOffset += comboOffset; - return new ConvertSpinner { Duration = duration diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index 56790629b4..8b8bc2f4e1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -13,7 +13,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X { get; set; } public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 014494ec54..3355bb3ee5 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -17,7 +17,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X => 256; // Required for CatchBeatmapConverter public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 069366bad3..03b103b8fe 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo + internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset { public Vector2 Position { get; set; } @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int ComboOffset { get; set; } + public int LegacyBeatmapComboOffset { get; set; } + + int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 75ecab0b8f..451b714266 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - ComboOffset = comboOffset + LegacyBeatmapComboOffset = comboOffset }; } @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - ComboOffset = comboOffset, + LegacyBeatmapComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index e947690668..c76c5a3f67 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset { public Vector2 Position { get; set; } @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int ComboOffset { get; set; } + public int LegacyBeatmapComboOffset { get; set; } + + int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index e9e5ca8c94..328a380a96 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -22,7 +22,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public float Y => Position.Y; public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs index d1a4683a1d..7288684d27 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs @@ -12,10 +12,5 @@ namespace osu.Game.Rulesets.Objects.Types /// Whether the HitObject starts a new combo. /// bool NewCombo { get; } - - /// - /// When starting a new combo, the offset of the new combo relative to the current one. - /// - int ComboOffset { get; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs b/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs new file mode 100644 index 0000000000..1a39192438 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A type of that has a combo index with arbitrary offsets applied to use when retrieving legacy beatmap combo colours. + /// This is done in stable for hitobjects to skip combo colours from the beatmap skin (known as "colour hax"). + /// See https://osu.ppy.sh/wiki/en/osu%21_File_Formats/Osu_%28file_format%29#type for more information. + /// + public interface IHasLegacyBeatmapComboOffset + { + /// + /// The legacy offset of the new combo relative to the current one, when starting a new combo. + /// + int LegacyBeatmapComboOffset { get; } + + /// + /// The combo index with the applied, + /// to use for legacy beatmap skins to decide on the combo colour. + /// + int LegacyBeatmapComboIndex { get; set; } + } +} From 51ff59242d73cd6f7b83d1fa6059aeac068b168d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:36:30 +0300 Subject: [PATCH 006/489] Use legacy beatmap combo indices for legacy beatmap skins --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 3ec205e897..7262169742 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -6,8 +6,11 @@ using osu.Framework.Bindables; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets.Objects.Legacy; +using osu.Game.Rulesets.Objects.Types; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -39,6 +42,14 @@ namespace osu.Game.Skinning return base.GetConfig(lookup); } + protected override IBindable GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) + { + if (combo is IHasLegacyBeatmapComboOffset legacyBeatmapCombo) + return base.GetComboColour(source, legacyBeatmapCombo.LegacyBeatmapComboIndex, combo); + + return base.GetComboColour(source, comboIndex, combo); + } + public override ISample GetSample(ISampleInfo sampleInfo) { if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0) From fda6d8685caa8facab80bc66e129040055b432c1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 09:21:09 +0300 Subject: [PATCH 007/489] Let `LegacyBeatmapSkinColourTest` inherit from `TestPlayer` instead --- osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 051ede30b7..a38806aa71 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Screens.Play; using osu.Game.Skinning; using osu.Game.Tests.Visual; using osuTK.Graphics; @@ -66,16 +65,12 @@ namespace osu.Game.Tests.Beatmaps protected virtual ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new ExposedPlayer(userHasCustomColours); - protected class ExposedPlayer : Player + protected class ExposedPlayer : TestPlayer { protected readonly bool UserHasCustomColours; public ExposedPlayer(bool userHasCustomColours) - : base(new PlayerConfiguration - { - AllowPause = false, - ShowResults = false, - }) + : base(false, false) { UserHasCustomColours = userHasCustomColours; } From 4cdfdcaddf00a3ce24ecdd40ae486cd8b39fce50 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 09:21:38 +0300 Subject: [PATCH 008/489] Expand the combo colours used for testing To avoid potential false positive. --- osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index a38806aa71..2315bbf709 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -107,6 +107,8 @@ namespace osu.Game.Tests.Beatmaps { new Color4(50, 100, 150, 255), new Color4(40, 80, 120, 255), + new Color4(25, 50, 75, 255), + new Color4(10, 20, 30, 255), }; public static readonly Color4 HYPER_DASH_COLOUR = Color4.DarkBlue; @@ -134,6 +136,8 @@ namespace osu.Game.Tests.Beatmaps { new Color4(150, 100, 50, 255), new Color4(20, 20, 20, 255), + new Color4(75, 50, 25, 255), + new Color4(80, 80, 80, 255), }; public static readonly Color4 HYPER_DASH_COLOUR = Color4.LightBlue; From 71d3b44c78cd3256aed8bd79b3b8593ef147f281 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 09:22:19 +0300 Subject: [PATCH 009/489] Add test coverage for legacy beatmap combo offsets --- .../TestSceneLegacyBeatmapSkin.cs | 86 +++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index c26419b0e8..c24e54b4e8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -11,6 +13,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Tests { @@ -77,23 +80,96 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void TestLegacyOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) + { + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); + base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + + assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", + TestBeatmapSkin.Colours, + (i, obj) => i + 1 + obj.LegacyBeatmapComboOffset); + } + + [TestCase(true)] + [TestCase(false)] + public void TestLegacyOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) + { + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); + base.TestBeatmapComboColoursOverride(useBeatmapSkin); + + assertCorrectObjectComboColours("is user skin colours without legacy offsets applied", + TestSkin.Colours, + (i, _) => i + 1); + } + + private void assertCorrectObjectComboColours(string description, Color4[] expectedColours, Func nextExpectedComboIndex) + { + AddUntilStep("wait for objects to become alive", () => + TestPlayer.DrawableRuleset.Playfield.AllHitObjects.Count() == TestPlayer.DrawableRuleset.Objects.Count()); + + AddAssert(description, () => + { + int index = 0; + + foreach (var drawable in TestPlayer.DrawableRuleset.Playfield.AllHitObjects) + { + index = nextExpectedComboIndex(index, (OsuHitObject)drawable.HitObject); + + if (drawable.AccentColour.Value != expectedColours[index % expectedColours.Length]) + return false; + } + + return true; + }); + } + + private static IEnumerable getHitCirclesWithLegacyOffsets() + { + var hitObjects = new List(); + + for (int i = 0; i < 5; i++) + { + hitObjects.Add(new HitCircle + { + StartTime = i, + Position = new Vector2(256, 192), + NewCombo = true, + LegacyBeatmapComboOffset = i, + }); + } + + return hitObjects; + } + private class OsuCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap { - public OsuCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) - : base(createBeatmap(), audio, hasColours) + public OsuCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours, IEnumerable hitObjects = null) + : base(createBeatmap(hitObjects), audio, hasColours) { } - private static IBeatmap createBeatmap() => - new Beatmap + private static IBeatmap createBeatmap(IEnumerable hitObjects) + { + var beatmap = new Beatmap { BeatmapInfo = { BeatmapSet = new BeatmapSetInfo(), Ruleset = new OsuRuleset().RulesetInfo, }, - HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } }; + + beatmap.HitObjects.AddRange(hitObjects ?? new[] + { + new HitCircle { Position = new Vector2(256, 192) } + }); + + return beatmap; + } } } } From bf6e98345c53fbaf0eb61e8d6542516567aa2b12 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 14:18:51 +0300 Subject: [PATCH 010/489] Remove and update pre-existing test cases --- .../Formats/LegacyBeatmapDecoderTest.cs | 39 ++++--------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 0f82492e51..be88bb9b43 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -14,8 +14,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; using osu.Game.IO; -using osu.Game.Rulesets.Catch; -using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; @@ -310,7 +308,7 @@ namespace osu.Game.Tests.Beatmaps.Formats } [Test] - public void TestDecodeBeatmapComboOffsetsOsu() + public void TestDecodeLegacyBeatmapComboOffsets() { var decoder = new LegacyBeatmapDecoder(); @@ -323,35 +321,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuBeatmapProcessor(converted).PreProcess(); new OsuBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndex); - } - } - - [Test] - public void TestDecodeBeatmapComboOffsetsCatch() - { - var decoder = new LegacyBeatmapDecoder(); - - using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) - using (var stream = new LineBufferedReader(resStream)) - { - var beatmap = decoder.Decode(stream); - - var converted = new CatchBeatmapConverter(beatmap, new CatchRuleset()).Convert(); - new CatchBeatmapProcessor(converted).PreProcess(); - new CatchBeatmapProcessor(converted).PostProcess(); - - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndex); + Assert.AreEqual(4, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(0)).LegacyBeatmapComboIndex); + Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(2)).LegacyBeatmapComboIndex); + Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(4)).LegacyBeatmapComboIndex); + Assert.AreEqual(6, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(6)).LegacyBeatmapComboIndex); + Assert.AreEqual(11, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(8)).LegacyBeatmapComboIndex); + Assert.AreEqual(14, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(11)).LegacyBeatmapComboIndex); } } From 6fb9eb8b33227284c889760d6ba45a22408f04e4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 14:24:13 +0300 Subject: [PATCH 011/489] Move legacy beatmap combo offset to osu! processor Better suited there, I intiailly wanted the whole legacy interface to reside in `osu.Game.Rulesets.Osu` but it's required in `ConvertHitObjectParser` and that's in the main game project, so had to leave the interface as-is for now. --- .../Beatmaps/OsuBeatmapProcessor.cs | 19 +++++++++++++++++++ osu.Game/Beatmaps/BeatmapProcessor.cs | 10 ---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index f51f04bf87..5bff3c7bcc 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; @@ -19,6 +21,23 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { } + public override void PreProcess() + { + base.PreProcess(); + + OsuHitObject lastObj = null; + + foreach (var obj in Beatmap.HitObjects.OfType()) + { + if (obj.NewCombo) + obj.LegacyBeatmapComboIndex = (lastObj?.LegacyBeatmapComboIndex ?? 0) + obj.LegacyBeatmapComboOffset + 1; + else if (lastObj != null) + obj.LegacyBeatmapComboIndex = lastObj.LegacyBeatmapComboIndex; + + lastObj = obj; + } + } + public override void PostProcess() { base.PostProcess(); diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index ac2d7d0fc1..f75f04b26f 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -48,16 +48,6 @@ namespace osu.Game.Beatmaps obj.ComboIndex = lastObj.ComboIndex; } - if (obj is IHasLegacyBeatmapComboOffset legacyObj) - { - var lastLegacyObj = (IHasLegacyBeatmapComboOffset)lastObj; - - if (obj.NewCombo) - legacyObj.LegacyBeatmapComboIndex = (lastLegacyObj?.LegacyBeatmapComboIndex ?? 0) + legacyObj.LegacyBeatmapComboOffset + 1; - else if (lastLegacyObj != null) - legacyObj.LegacyBeatmapComboIndex = lastLegacyObj.LegacyBeatmapComboIndex; - } - lastObj = obj; } } From cc24d8a6bd9627645de6afec29d514ad9e11e884 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 14:27:37 +0300 Subject: [PATCH 012/489] Remove unused using directive I literally noticed it after I pushed, god damn it. --- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 5bff3c7bcc..b7ec8795ca 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; From fe099be443b7ce7f2552cfa9958752197a43ebea Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 19 May 2021 22:25:56 +0800 Subject: [PATCH 013/489] Implemented osu target mod --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 367 ++++++++++++++++++++- 1 file changed, 366 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 2464308347..667fc91142 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -1,13 +1,34 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModTarget : Mod + public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, + IApplicableToHealthProcessor, IApplicableToDifficulty { public override string Name => "Target"; public override string Acronym => "TP"; @@ -15,5 +36,349 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage? Icon => OsuIcon.ModTarget; public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; + + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) + { + // Sudden death + healthProcessor.FailConditions += (_, result) + => result.Type.AffectsCombo() + && !result.IsHit; + } + + // Maximum distance to jump + public const float MAX_DISTANCE = 250f; + + public override void ApplyToBeatmap(IBeatmap beatmap) + { + base.ApplyToBeatmap(beatmap); + + var osuBeatmap = (OsuBeatmap)beatmap; + var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); + + var hitObjects = new List(); + + // Only place circles between startTime and endTime + var startTime = origHitObjects.First().StartTime; + double endTime; + var endObj = origHitObjects.Last(); + switch (endObj) + { + case Slider slider: + endTime = slider.EndTime; + break; + case Spinner spinner: + endTime = spinner.EndTime; + break; + default: + endTime = endObj.StartTime; + break; + } + + var comboStarts = origHitObjects.Where(x => x.NewCombo).Select(x => x.StartTime).ToList(); + + TimingControlPoint currentTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(startTime); + var currentTime = currentTimingPoint.Time; + int currentCombo = -1; + IList lastSamples = null; + + float direction = MathHelper.TwoPi * RNG.NextSingle(); + float distance = 40f; + + while (!Precision.AlmostBigger(currentTime, endTime)) + { + // Place a circle + + // Don't place any circles before the start of the first hit object of the map + // Don't place any circles from the start of break time to the start of the first hit object after the break + if (!Precision.AlmostBigger(startTime, currentTime) + && !osuBeatmap.Breaks.Any(x => Precision.AlmostBigger(currentTime, x.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, x.EndTime)).StartTime, currentTime))) + { + var newCircle = new HitCircle(); + newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.StartTime = currentTime; + + // Determine circle position + if (hitObjects.Count == 0) + { + newCircle.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); + } + else + { + var relativePos = new Vector2( + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); + relativePos = getRotatedVector(hitObjects.Last().Position, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + + var newPosition = Vector2.Add(hitObjects.Last().Position, relativePos); + + if (newPosition.Y < 0) + newPosition.Y = 0; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y; + if (newPosition.X < 0) + newPosition.X = 0; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) + newPosition.X = OsuPlayfield.BASE_SIZE.X; + + newCircle.Position = newPosition; + // Add a random nudge to the direction + direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); + } + + // Determine samples to use + var samples = getSamplesAtTime(origHitObjects, currentTime); + if (samples == null) + { + if (lastSamples != null) + newCircle.Samples = lastSamples; + } + else + { + newCircle.Samples = samples; + lastSamples = samples; + } + + // Determine combo + if (comboStarts.Count > currentCombo + 1 && !Precision.AlmostBigger(comboStarts[currentCombo + 1], currentTime)) + { + currentCombo++; + newCircle.NewCombo = true; + newCircle.IndexInCurrentCombo = 0; + if (hitObjects.Count > 0) + hitObjects.Last().LastInCombo = true; + + // Increase distance for every combo + distance = Math.Min(distance * 1.05f, MAX_DISTANCE); + // Randomize direction as well + direction = MathHelper.TwoPi * RNG.NextSingle(); + } + else + { + if (hitObjects.Count > 0) + newCircle.IndexInCurrentCombo = hitObjects.Last().IndexInCurrentCombo + 1; + } + newCircle.ComboIndex = currentCombo; + + // Remove the last circle if it is too close in time to this one + if (hitObjects.Count > 0 + && Precision.AlmostBigger(currentTimingPoint.BeatLength / 2, newCircle.StartTime - hitObjects.Last().StartTime)) + { + hitObjects.RemoveAt(hitObjects.Count - 1); + } + + hitObjects.Add(newCircle); + } + + // Advance to the next beat and check for timing point changes + + currentTime += currentTimingPoint.BeatLength; + + var newTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(currentTime); + if (newTimingPoint != currentTimingPoint) + { + currentTimingPoint = newTimingPoint; + currentTime = currentTimingPoint.Time; + } + } + + osuBeatmap.HitObjects = hitObjects; + } + + /// + /// Get samples (if any) for a specific point in time. + /// + /// + /// Samples will be returned if a hit circle or a slider node exists at that point of time. + /// + /// The list of hit objects in a beatmap, ordered by StartTime + /// The point in time to get samples for + /// Hit samples + private IList getSamplesAtTime(List hitObjects, double time) + { + var sampleObj = hitObjects.FirstOrDefault(x => + { + if (Precision.AlmostEquals(time, x.StartTime)) return true; + if (x is Slider slider + && Precision.AlmostBigger(time, slider.StartTime) + && Precision.AlmostBigger(slider.EndTime, time)) + { + if (Precision.AlmostEquals((time - slider.StartTime) % slider.SpanDuration, 0)) + { + return true; + } + } + return false; + }); + if (sampleObj == null) return null; + + IList samples = null; + if (sampleObj is Slider slider) + { + samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; + } + else + { + samples = sampleObj.Samples; + } + return samples; + } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) + { + if (drawable is DrawableSpinner) + return; + + var h = (OsuHitObject)drawable.HitObject; + + // apply grow and fade effect + if (drawable is DrawableHitCircle circle) + { + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + drawable.ScaleTo(0.4f) + .Then().ScaleTo(1.6f, h.TimePreempt * 2); + drawable.FadeTo(0.5f) + .Then().Delay(h.TimeFadeIn).FadeTo(1f); + + // remove approach circles + circle.ApproachCircle.Hide(); + } + } + } + + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + // Decrease AR to increase preempt time + difficulty.ApproachRate *= 0.5f; + } + + // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The closer the hit objects draw to the border, the sharper the turn + private const byte border_distance_x = 192; + private const byte border_distance_y = 144; + + /// + /// Determines the position of the current hit object relative to the previous one. + /// + /// The position of the current hit object relative to the previous one + private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) + { + var relativeRotationDistance = 0f; + var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); + + if (prevPosChanged.X < playfieldMiddle.X) + { + relativeRotationDistance = Math.Max( + (border_distance_x - prevPosChanged.X) / border_distance_x, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x, + relativeRotationDistance + ); + } + + if (prevPosChanged.Y < playfieldMiddle.Y) + { + relativeRotationDistance = Math.Max( + (border_distance_y - prevPosChanged.Y) / border_distance_y, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y, + relativeRotationDistance + ); + } + + return rotateVectorTowardsVector( + posRelativeToPrev, + Vector2.Subtract(playfieldMiddle, prevPosChanged), + relativeRotationDistance / 2 + ); + } + + /// + /// Rotates vector "initial" towards vector "destinantion" + /// + /// Vector to rotate to "destination" + /// Vector "initial" should be rotated to + /// The angle the vector should be rotated relative to the difference between the angles of the the two vectors. + /// Resulting vector + private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) + { + var initialAngleRad = Math.Atan2(initial.Y, initial.X); + var destAngleRad = Math.Atan2(destination.Y, destination.X); + + var diff = destAngleRad - initialAngleRad; + + while (diff < -Math.PI) + { + diff += 2 * Math.PI; + } + + while (diff > Math.PI) + { + diff -= 2 * Math.PI; + } + + var finalAngleRad = initialAngleRad + relativeDistance * diff; + + return new Vector2( + initial.Length * (float)Math.Cos(finalAngleRad), + initial.Length * (float)Math.Sin(finalAngleRad) + ); + } + + // Background metronome + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Overlays.Add(new TargetBeatContainer()); + } + + public class TargetBeatContainer : BeatSyncedContainer + { + private PausableSkinnableSound sample; + + public TargetBeatContainer() + { + Divisor = 1; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // TODO: use another sample? + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + sample?.Play(); + } + } } } From d20b5c2d5aa9865bb302fe17b4a541f237b436a1 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 20 May 2021 11:57:13 +0800 Subject: [PATCH 014/489] Refactored ApplyToBeatmap --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 202 +++++++++++---------- 1 file changed, 105 insertions(+), 97 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 667fc91142..412b7049ea 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -55,8 +55,6 @@ namespace osu.Game.Rulesets.Osu.Mods var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); - var hitObjects = new List(); - // Only place circles between startTime and endTime var startTime = origHitObjects.First().StartTime; double endTime; @@ -74,113 +72,122 @@ namespace osu.Game.Rulesets.Osu.Mods break; } - var comboStarts = origHitObjects.Where(x => x.NewCombo).Select(x => x.StartTime).ToList(); - - TimingControlPoint currentTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(startTime); - var currentTime = currentTimingPoint.Time; - int currentCombo = -1; - IList lastSamples = null; - - float direction = MathHelper.TwoPi * RNG.NextSingle(); - float distance = 40f; - - while (!Precision.AlmostBigger(currentTime, endTime)) - { - // Place a circle - - // Don't place any circles before the start of the first hit object of the map - // Don't place any circles from the start of break time to the start of the first hit object after the break - if (!Precision.AlmostBigger(startTime, currentTime) - && !osuBeatmap.Breaks.Any(x => Precision.AlmostBigger(currentTime, x.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, x.EndTime)).StartTime, currentTime))) + // Generate the beats + var beats = osuBeatmap.ControlPointInfo.TimingPoints + .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .SelectMany(tp => + { + var tpBeats = new List(); + var currentTime = tp.Time; + while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) + { + tpBeats.Add(currentTime); + currentTime += tp.BeatLength; + } + return tpBeats; + }) + // Remove beats that are before startTime + .Where(x => Precision.AlmostBigger(x, startTime)) + // Remove beats during breaks + .Where(x => !osuBeatmap.Breaks.Any(b => + Precision.AlmostBigger(x, b.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) + )) + .ToList(); + // Generate a hit circle for each beat + var hitObjects = beats + // Remove beats that are too close to the next one (e.g. due to timing point changes) + .Where((x, idx) => + { + if (idx == beats.Count - 1) return true; + if (Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x)) + return false; + return true; + }) + .Select(x => { var newCircle = new HitCircle(); newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = currentTime; + newCircle.StartTime = x; + return (OsuHitObject)newCircle; + }).ToList(); - // Determine circle position - if (hitObjects.Count == 0) - { - newCircle.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); - } - else - { - var relativePos = new Vector2( + // Add hit samples to the circles + for (int i = 0; i < hitObjects.Count; i++) + { + var x = hitObjects[i]; + var samples = getSamplesAtTime(origHitObjects, x.StartTime); + if (samples == null) + { + if (i > 0) + x.Samples = hitObjects[i - 1].Samples; + } + else + { + x.Samples = samples; + } + } + + // Process combo numbers + // First follow the combo indices in the original beatmap + hitObjects.ForEach(x => + { + var origObj = origHitObjects.FindLast(y => Precision.AlmostBigger(x.StartTime, y.StartTime)); + if (origObj == null) x.ComboIndex = 0; + else x.ComboIndex = origObj.ComboIndex; + }); + // Then reprocess them to ensure continuity in the combo indices and add indices in current combo + var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); + for (int i = 0; i < combos.Count; i++) + { + var group = combos[i].ToList(); + group.First().NewCombo = true; + group.Last().LastInCombo = true; + + for (int j = 0; j < group.Count; j++) + { + var x = group[j]; + x.ComboIndex = i; + x.IndexInCurrentCombo = j; + } + } + + // Position all hit circles + var direction = MathHelper.TwoPi * RNG.NextSingle(); + for (int i = 0; i < hitObjects.Count; i++) + { + var x = hitObjects[i]; + if (i == 0) + { + x.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); + } + else + { + var distance = Math.Min(MAX_DISTANCE, 40f * (float)Math.Pow(1.05, x.ComboIndex)); + var relativePos = new Vector2( distance * (float)Math.Cos(direction), distance * (float)Math.Sin(direction) ); - relativePos = getRotatedVector(hitObjects.Last().Position, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + relativePos = getRotatedVector(hitObjects[i - 1].Position, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); - var newPosition = Vector2.Add(hitObjects.Last().Position, relativePos); + var newPosition = Vector2.Add(hitObjects[i - 1].Position, relativePos); - if (newPosition.Y < 0) - newPosition.Y = 0; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y; - if (newPosition.X < 0) - newPosition.X = 0; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) - newPosition.X = OsuPlayfield.BASE_SIZE.X; + if (newPosition.Y < 0) + newPosition.Y = 0; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y; + if (newPosition.X < 0) + newPosition.X = 0; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) + newPosition.X = OsuPlayfield.BASE_SIZE.X; - newCircle.Position = newPosition; - // Add a random nudge to the direction - direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); - } + x.Position = newPosition; - // Determine samples to use - var samples = getSamplesAtTime(origHitObjects, currentTime); - if (samples == null) - { - if (lastSamples != null) - newCircle.Samples = lastSamples; - } - else - { - newCircle.Samples = samples; - lastSamples = samples; - } - - // Determine combo - if (comboStarts.Count > currentCombo + 1 && !Precision.AlmostBigger(comboStarts[currentCombo + 1], currentTime)) - { - currentCombo++; - newCircle.NewCombo = true; - newCircle.IndexInCurrentCombo = 0; - if (hitObjects.Count > 0) - hitObjects.Last().LastInCombo = true; - - // Increase distance for every combo - distance = Math.Min(distance * 1.05f, MAX_DISTANCE); - // Randomize direction as well + if (x.LastInCombo) direction = MathHelper.TwoPi * RNG.NextSingle(); - } else - { - if (hitObjects.Count > 0) - newCircle.IndexInCurrentCombo = hitObjects.Last().IndexInCurrentCombo + 1; - } - newCircle.ComboIndex = currentCombo; - - // Remove the last circle if it is too close in time to this one - if (hitObjects.Count > 0 - && Precision.AlmostBigger(currentTimingPoint.BeatLength / 2, newCircle.StartTime - hitObjects.Last().StartTime)) - { - hitObjects.RemoveAt(hitObjects.Count - 1); - } - - hitObjects.Add(newCircle); - } - - // Advance to the next beat and check for timing point changes - - currentTime += currentTimingPoint.BeatLength; - - var newTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(currentTime); - if (newTimingPoint != currentTimingPoint) - { - currentTimingPoint = newTimingPoint; - currentTime = currentTimingPoint.Time; + direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } @@ -242,6 +249,7 @@ namespace osu.Game.Rulesets.Osu.Mods { using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) { + // todo: this doesn't feel quite right yet drawable.ScaleTo(0.4f) .Then().ScaleTo(1.6f, h.TimePreempt * 2); drawable.FadeTo(0.5f) @@ -367,7 +375,7 @@ namespace osu.Game.Rulesets.Osu.Mods { InternalChildren = new Drawable[] { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // TODO: use another sample? + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // todo: use another sample? }; } From 15e7cce264ed3c31cf03a29a7cadd241abbf030f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 22 May 2021 14:14:41 +0800 Subject: [PATCH 015/489] Added seed setting, fixed mod ignoring IncreaseFirstObjectVisibility --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 116 +++++++++++++++++++-- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 412b7049ea..3fe0161742 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -6,15 +6,23 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Multiplayer; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; @@ -37,6 +45,13 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; + [SettingSource("Seed", "Seed for random circle placement", SettingControlType = typeof(OsuModTargetSettingsControl))] + public Bindable Seed { get; } = new Bindable + { + Default = null, + Value = null + }; + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { // Sudden death @@ -50,7 +65,14 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToBeatmap(IBeatmap beatmap) { - base.ApplyToBeatmap(beatmap); + Seed.Value ??= RNG.Next(); + + var rng = new Random(Seed.Value.GetValueOrDefault()); + + float nextSingle(float max = 1f) + { + return (float)(rng.NextDouble() * max); + } var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); @@ -153,13 +175,13 @@ namespace osu.Game.Rulesets.Osu.Mods } // Position all hit circles - var direction = MathHelper.TwoPi * RNG.NextSingle(); + var direction = MathHelper.TwoPi * nextSingle(); for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; if (i == 0) { - x.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); + x.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); } else { @@ -185,13 +207,15 @@ namespace osu.Game.Rulesets.Osu.Mods x.Position = newPosition; if (x.LastInCombo) - direction = MathHelper.TwoPi * RNG.NextSingle(); + direction = MathHelper.TwoPi * nextSingle(); else - direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / MAX_DISTANCE * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } osuBeatmap.HitObjects = hitObjects; + + base.ApplyToBeatmap(beatmap); } /// @@ -233,7 +257,7 @@ namespace osu.Game.Rulesets.Osu.Mods return samples; } - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) { } @@ -389,4 +413,84 @@ namespace osu.Game.Rulesets.Osu.Mods } } } + + public class OsuModTargetSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set + { + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 120) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true, + }, + new TriangleButton + { + Width = 120, + Text = "Randomise", + Action = () => current.Value = RNG.Next(), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + } + } + } + }; + + seedNumberBox.Current.BindValueChanged(e => + { + if (int.TryParse(e.NewValue, out var intVal)) + current.Value = intVal; + else + current.Value = null; + }); + current.BindValueChanged(e => + { + seedNumberBox.Text = e.NewValue.ToString(); + }); + } + } + } } From 7815b3c72bed5856e52803d646097fdffc56e668 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 10 Jun 2021 10:58:42 +0800 Subject: [PATCH 016/489] Display results after fail --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 ++ osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 8 +++++++- osu.Game/Rulesets/Mods/IApplicableFailOverride.cs | 5 +++++ osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 ++ osu.Game/Rulesets/Mods/ModBlockFail.cs | 1 + osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs | 1 + osu.Game/Rulesets/Mods/ModFailCondition.cs | 1 + osu.Game/Screens/Play/Player.cs | 10 ++++++++-- 8 files changed, 27 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index aac830801b..2b92174b29 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Mods public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; + private OsuInputManager inputManager; private IFrameStableClock gameplayClock; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3fe0161742..c3e9e60420 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -36,7 +36,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride { public override string Name => "Target"; public override string Acronym => "TP"; @@ -52,6 +52,12 @@ namespace osu.Game.Rulesets.Osu.Mods Value = null }; + public bool PerformFail() => true; + + public bool RestartOnFail => false; + + public bool DisplayResultsOnFail => true; + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { // Sudden death diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index 8c99d739cb..b7d306e57b 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -18,5 +18,10 @@ namespace osu.Game.Rulesets.Mods /// Whether we want to restart on fail. Only used if returns true. /// bool RestartOnFail { get; } + + /// + /// Whether to proceed to results screen on fail. Only used if returns true and is false. + /// + bool DisplayResultsOnFail { get; } } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index d6e1d46b06..3fb92527f2 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -35,6 +35,8 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; + public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) }; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 1fde5abad4..973a192378 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Mods public bool PerformFail() => false; public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; public void ReadFromConfig(OsuConfigManager config) { diff --git a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs index 2ac0f59d84..91508b7e70 100644 --- a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs +++ b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Mods } public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index c0d7bae2b2..012a0f73ff 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; public virtual bool RestartOnFail => true; + public virtual bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 890883f0d2..b72524ad99 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -604,7 +604,7 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - if (!ScoreProcessor.HasCompleted.Value) + if (!ScoreProcessor.HasCompleted.Value && !HealthProcessor.HasFailed) { completionProgressDelegate?.Cancel(); completionProgressDelegate = null; @@ -617,7 +617,7 @@ namespace osu.Game.Screens.Play throw new InvalidOperationException($"{nameof(updateCompletionState)} was fired more than once"); // Only show the completion screen if the player hasn't failed - if (HealthProcessor.HasFailed) + if (HealthProcessor.HasFailed && !Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) return; ValidForResume = false; @@ -712,6 +712,12 @@ namespace osu.Game.Screens.Play // Called back when the transform finishes private void onFailComplete() { + if (Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) + { + updateCompletionState(true); + return; + } + GameplayClockContainer.Stop(); FailOverlay.Retries = RestartCount; From 12a17d0983c6f3557ef7c8231fca247f8311181f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 10 Jun 2021 15:31:20 +0800 Subject: [PATCH 017/489] Extract seed setting contorl to IHasSeed --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 90 +------------------- osu.Game/Rulesets/Mods/IHasSeed.cs | 95 ++++++++++++++++++++++ osu.Game/Rulesets/Mods/ModRandom.cs | 81 +----------------- 3 files changed, 99 insertions(+), 167 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/IHasSeed.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index c3e9e60420..04a7365148 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -8,9 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -18,11 +16,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; @@ -36,7 +30,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed { public override string Name => "Target"; public override string Acronym => "TP"; @@ -45,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - [SettingSource("Seed", "Seed for random circle placement", SettingControlType = typeof(OsuModTargetSettingsControl))] + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable { Default = null, @@ -419,84 +413,4 @@ namespace osu.Game.Rulesets.Osu.Mods } } } - - public class OsuModTargetSettingsControl : SettingsItem - { - protected override Drawable CreateControl() => new SeedControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; - - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current - { - get => current.Current; - set - { - current.Current = value; - seedNumberBox.Text = value.Value.ToString(); - } - } - - private readonly OsuNumberBox seedNumberBox; - - public SeedControl() - { - AutoSizeAxes = Axes.Y; - - InternalChildren = new[] - { - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 120) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] - { - seedNumberBox = new OsuNumberBox - { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true, - }, - new TriangleButton - { - Width = 120, - Text = "Randomise", - Action = () => current.Value = RNG.Next(), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - } - } - } - }; - - seedNumberBox.Current.BindValueChanged(e => - { - if (int.TryParse(e.NewValue, out var intVal)) - current.Value = intVal; - else - current.Value = null; - }); - current.BindValueChanged(e => - { - seedNumberBox.Text = e.NewValue.ToString(); - }); - } - } - } } diff --git a/osu.Game/Rulesets/Mods/IHasSeed.cs b/osu.Game/Rulesets/Mods/IHasSeed.cs new file mode 100644 index 0000000000..45a806e80c --- /dev/null +++ b/osu.Game/Rulesets/Mods/IHasSeed.cs @@ -0,0 +1,95 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Rulesets.Mods +{ + public interface IHasSeed + { + public Bindable Seed { get; } + } + + public class SeedSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current; + set + { + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true + } + } + } + } + }; + + seedNumberBox.Current.BindValueChanged(e => + { + int? value = null; + + if (int.TryParse(e.NewValue, out var intVal)) + value = intVal; + + current.Value = value; + }); + } + + protected override void Update() + { + if (current.Value == null) + seedNumberBox.Text = current.Current.Value.ToString(); + } + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index 3f14263420..eec66161ff 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -13,7 +13,7 @@ using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { - public abstract class ModRandom : Mod + public abstract class ModRandom : Mod, IHasSeed { public override string Name => "Random"; public override string Acronym => "RD"; @@ -21,88 +21,11 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.Dice; public override double ScoreMultiplier => 1; - [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(ModRandomSettingsControl))] + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable { Default = null, Value = null }; - - private class ModRandomSettingsControl : SettingsItem - { - protected override Drawable CreateControl() => new SeedControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; - - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current - { - get => current; - set - { - current.Current = value; - seedNumberBox.Text = value.Value.ToString(); - } - } - - private readonly OsuNumberBox seedNumberBox; - - public SeedControl() - { - AutoSizeAxes = Axes.Y; - - InternalChildren = new[] - { - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 2), - new Dimension(GridSizeMode.Relative, 0.25f) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] - { - seedNumberBox = new OsuNumberBox - { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true - } - } - } - } - }; - - seedNumberBox.Current.BindValueChanged(e => - { - int? value = null; - - if (int.TryParse(e.NewValue, out var intVal)) - value = intVal; - - current.Value = value; - }); - } - - protected override void Update() - { - if (current.Value == null) - seedNumberBox.Text = current.Current.Value.ToString(); - } - } - } } } From 3309ab2be32441415d3432aa6d0476a8b1604bb0 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Sun, 13 Jun 2021 15:18:35 +0200 Subject: [PATCH 018/489] balance changes --- .../Difficulty/OsuDifficultyCalculator.cs | 4 +- .../Difficulty/OsuPerformanceCalculator.cs | 7 ++- .../Difficulty/Skills/Aim.cs | 4 +- .../Difficulty/Skills/OsuSkill.cs | 44 +++++++++++++++++++ .../Difficulty/Skills/Speed.cs | 2 +- 5 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 75d6786d95..b83e504a7a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (beatmap.HitObjects.Count == 0) return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; - double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double aimRating = Math.Sqrt((skills[0] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt((skills[1] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 44a9dd2f1f..90ded2b837 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -106,22 +106,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty else if (Attributes.ApproachRate < 8.0) approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate); - aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); + approachRateFactor = 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); + double flashlightBonus = 1.0; if (mods.Any(h => h is OsuModFlashlight)) { // Apply object-based bonus for flashlight. - aimValue *= 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + + flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + (totalHits > 200 ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) : 0.0); } + aimValue *= Math.Max(flashlightBonus, approachRateFactor); + // Scale the aim value with accuracy _slightly_ aimValue *= 0.5 + accuracy / 2.0; // It is important to also consider accuracy difficulty when doing that diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index cb819ec090..b7bb10a87e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. /// - public class Aim : StrainSkill + public class Aim : OsuSkill { private const double angle_bonus_begin = Math.PI / 3; private const double timing_threshold = 107; @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); - result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); + result = 1.35 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs new file mode 100644 index 0000000000..d113f7135f --- /dev/null +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; +using System.Linq; + +namespace osu.Game.Rulesets.Osu.Difficulty.Skills +{ + public abstract class OsuSkill : StrainSkill + { + public OsuSkill(Mod[] mods) : base(mods) + { + } + + public double OsuDifficultyValue() + { + double difficulty = 0; + double weight = 1; + + double strainMultiplier; + List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + + double baseLine = 0.6; + + for (int i = 0; i <= 9; i++) + { + strainMultiplier = baseLine + Math.Log10(i+1) * (1.0 - baseLine); + strains[i] = strains[i] * strainMultiplier; + } + + // Difficulty is the weighted sum of the highest strains from every section. + // We're sorting from highest to lowest strain. + foreach (double strain in strains.OrderByDescending(d => d)) + { + difficulty += strain * weight; + weight *= DecayWeight; + } + + return difficulty * 1.06; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index fbac080fc6..975a633bad 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. /// - public class Speed : StrainSkill + public class Speed : OsuSkill { private const double single_spacing_threshold = 125; From 4c949d9829ae58eddf0e7df9227d66b0fc189bcd Mon Sep 17 00:00:00 2001 From: emu1337 Date: Sun, 13 Jun 2021 21:20:08 +0200 Subject: [PATCH 019/489] reduced diffspike nerf --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs index d113f7135f..d52b95d3ec 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double strainMultiplier; List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); - double baseLine = 0.6; + double baseLine = 0.68; for (int i = 0; i <= 9; i++) { From 04c0db6dceb94b147c5dd8dad095ed0d97e39adc Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 14 Jun 2021 21:34:34 +0800 Subject: [PATCH 020/489] Code cleanup --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 161 ++++++++++----------- osu.Game/Rulesets/Mods/IHasSeed.cs | 54 +++---- osu.Game/Rulesets/Mods/ModRandom.cs | 5 - 3 files changed, 105 insertions(+), 115 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 04a7365148..0d123a4ce3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -30,7 +30,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed { public override string Name => "Target"; public override string Acronym => "TP"; @@ -57,11 +57,11 @@ namespace osu.Game.Rulesets.Osu.Mods // Sudden death healthProcessor.FailConditions += (_, result) => result.Type.AffectsCombo() - && !result.IsHit; + && !result.IsHit; } // Maximum distance to jump - public const float MAX_DISTANCE = 250f; + private const float max_distance = 250f; public override void ApplyToBeatmap(IBeatmap beatmap) { @@ -69,76 +69,68 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random(Seed.Value.GetValueOrDefault()); - float nextSingle(float max = 1f) - { - return (float)(rng.NextDouble() * max); - } + float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); // Only place circles between startTime and endTime var startTime = origHitObjects.First().StartTime; - double endTime; var endObj = origHitObjects.Last(); - switch (endObj) + var endTime = endObj switch { - case Slider slider: - endTime = slider.EndTime; - break; - case Spinner spinner: - endTime = spinner.EndTime; - break; - default: - endTime = endObj.StartTime; - break; - } + Slider slider => slider.EndTime, + Spinner spinner => spinner.EndTime, + _ => endObj.StartTime + }; // Generate the beats var beats = osuBeatmap.ControlPointInfo.TimingPoints - .Where(x => Precision.AlmostBigger(endTime, x.Time)) - .SelectMany(tp => - { - var tpBeats = new List(); - var currentTime = tp.Time; - while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) - { - tpBeats.Add(currentTime); - currentTime += tp.BeatLength; - } - return tpBeats; - }) - // Remove beats that are before startTime - .Where(x => Precision.AlmostBigger(x, startTime)) - // Remove beats during breaks - .Where(x => !osuBeatmap.Breaks.Any(b => - Precision.AlmostBigger(x, b.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) - )) - .ToList(); + .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .SelectMany(tp => + { + var tpBeats = new List(); + var currentTime = tp.Time; + + while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) + { + tpBeats.Add(currentTime); + currentTime += tp.BeatLength; + } + + return tpBeats; + }) + // Remove beats that are before startTime + .Where(x => Precision.AlmostBigger(x, startTime)) + // Remove beats during breaks + .Where(x => !osuBeatmap.Breaks.Any(b => + Precision.AlmostBigger(x, b.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) + )) + .ToList(); // Generate a hit circle for each beat var hitObjects = beats - // Remove beats that are too close to the next one (e.g. due to timing point changes) - .Where((x, idx) => - { - if (idx == beats.Count - 1) return true; - if (Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x)) - return false; - return true; - }) - .Select(x => - { - var newCircle = new HitCircle(); - newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = x; - return (OsuHitObject)newCircle; - }).ToList(); + // Remove beats that are too close to the next one (e.g. due to timing point changes) + .Where((x, idx) => + { + if (idx == beats.Count - 1) return true; + + return !Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x); + }) + .Select(x => + { + var newCircle = new HitCircle(); + newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.StartTime = x; + return (OsuHitObject)newCircle; + }).ToList(); // Add hit samples to the circles for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; var samples = getSamplesAtTime(origHitObjects, x.StartTime); + if (samples == null) { if (i > 0) @@ -155,11 +147,11 @@ namespace osu.Game.Rulesets.Osu.Mods hitObjects.ForEach(x => { var origObj = origHitObjects.FindLast(y => Precision.AlmostBigger(x.StartTime, y.StartTime)); - if (origObj == null) x.ComboIndex = 0; - else x.ComboIndex = origObj.ComboIndex; + x.ComboIndex = origObj?.ComboIndex ?? 0; }); // Then reprocess them to ensure continuity in the combo indices and add indices in current combo var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); + for (int i = 0; i < combos.Count; i++) { var group = combos[i].ToList(); @@ -176,20 +168,22 @@ namespace osu.Game.Rulesets.Osu.Mods // Position all hit circles var direction = MathHelper.TwoPi * nextSingle(); + for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; + if (i == 0) { x.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); } else { - var distance = Math.Min(MAX_DISTANCE, 40f * (float)Math.Pow(1.05, x.ComboIndex)); + var distance = Math.Min(max_distance, 40f * (float)Math.Pow(1.05, x.ComboIndex)); var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) - ); + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); relativePos = getRotatedVector(hitObjects[i - 1].Position, relativePos); direction = (float)Math.Atan2(relativePos.Y, relativePos.X); @@ -209,7 +203,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (x.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); else - direction += distance / MAX_DISTANCE * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } @@ -227,25 +221,24 @@ namespace osu.Game.Rulesets.Osu.Mods /// The list of hit objects in a beatmap, ordered by StartTime /// The point in time to get samples for /// Hit samples - private IList getSamplesAtTime(List hitObjects, double time) + private IList getSamplesAtTime(IEnumerable hitObjects, double time) { var sampleObj = hitObjects.FirstOrDefault(x => { if (Precision.AlmostEquals(time, x.StartTime)) return true; - if (x is Slider slider - && Precision.AlmostBigger(time, slider.StartTime) - && Precision.AlmostBigger(slider.EndTime, time)) - { - if (Precision.AlmostEquals((time - slider.StartTime) % slider.SpanDuration, 0)) - { - return true; - } - } - return false; + + if (!(x is Slider s)) + return false; + if (!Precision.AlmostBigger(time, s.StartTime) + || !Precision.AlmostBigger(s.EndTime, time)) + return false; + + return Precision.AlmostEquals((time - s.StartTime) % s.SpanDuration, 0); }); if (sampleObj == null) return null; - IList samples = null; + IList samples; + if (sampleObj is Slider slider) { samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; @@ -254,6 +247,7 @@ namespace osu.Game.Rulesets.Osu.Mods { samples = sampleObj.Samples; } + return samples; } @@ -269,19 +263,18 @@ namespace osu.Game.Rulesets.Osu.Mods var h = (OsuHitObject)drawable.HitObject; // apply grow and fade effect - if (drawable is DrawableHitCircle circle) + if (!(drawable is DrawableHitCircle circle)) return; + + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) { - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - // todo: this doesn't feel quite right yet - drawable.ScaleTo(0.4f) + // todo: this doesn't feel quite right yet + drawable.ScaleTo(0.4f) .Then().ScaleTo(1.6f, h.TimePreempt * 2); - drawable.FadeTo(0.5f) + drawable.FadeTo(0.5f) .Then().Delay(h.TimeFadeIn).FadeTo(1f); - // remove approach circles - circle.ApproachCircle.Hide(); - } + // remove approach circles + circle.ApproachCircle.Hide(); } } @@ -347,7 +340,7 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Rotates vector "initial" towards vector "destinantion" + /// Rotates vector "initial" towards vector "destination" /// /// Vector to rotate to "destination" /// Vector "initial" should be rotated to @@ -399,7 +392,7 @@ namespace osu.Game.Rulesets.Osu.Mods { InternalChildren = new Drawable[] { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // todo: use another sample? + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample? }; } diff --git a/osu.Game/Rulesets/Mods/IHasSeed.cs b/osu.Game/Rulesets/Mods/IHasSeed.cs index 45a806e80c..13d59a9855 100644 --- a/osu.Game/Rulesets/Mods/IHasSeed.cs +++ b/osu.Game/Rulesets/Mods/IHasSeed.cs @@ -5,7 +5,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; @@ -18,11 +17,14 @@ namespace osu.Game.Rulesets.Mods public class SeedSettingsControl : SettingsItem { - protected override Drawable CreateControl() => new SeedControl + protected override Drawable CreateControl() { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; + return new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + } private sealed class SeedControl : CompositeDrawable, IHasCurrentValue { @@ -46,33 +48,33 @@ namespace osu.Game.Rulesets.Mods InternalChildren = new[] { - new GridContainer + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 2), - new Dimension(GridSizeMode.Relative, 0.25f) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] + seedNumberBox = new OsuNumberBox { - seedNumberBox = new OsuNumberBox - { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true - } + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true } } } - }; + } + }; seedNumberBox.Current.BindValueChanged(e => { diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index eec66161ff..61297c162d 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -2,14 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { From fea7b029aa31da7037d9aa91d330412ab800dd40 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Mon, 14 Jun 2021 19:18:49 +0200 Subject: [PATCH 021/489] refactored diffspike nerf --- .../Difficulty/OsuDifficultyCalculator.cs | 4 ++-- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- .../Skills/{OsuSkill.cs => OsuStrainSkill.cs} | 16 +++++++++------- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) rename osu.Game.Rulesets.Osu/Difficulty/Skills/{OsuSkill.cs => OsuStrainSkill.cs} (64%) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b83e504a7a..fdcc6f44a6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (beatmap.HitObjects.Count == 0) return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; - double aimRating = Math.Sqrt((skills[0] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt((skills[1] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; + double aimRating = Math.Sqrt((skills[0] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt((skills[1] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index b7bb10a87e..a95bf4fa94 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. /// - public class Aim : OsuSkill + public class Aim : OsuStrainSkill { private const double angle_bonus_begin = Math.PI / 3; private const double timing_threshold = 107; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs similarity index 64% rename from osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs rename to osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index d52b95d3ec..f1c7ae3403 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -8,9 +8,13 @@ using System.Linq; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { - public abstract class OsuSkill : StrainSkill + public abstract class OsuStrainSkill : StrainSkill { - public OsuSkill(Mod[] mods) : base(mods) + protected virtual int ReducedSectionCount => 9; + protected virtual double ReducedStrainBaseline => 0.68; + protected virtual double DifficultyMultiplier => 1.06; + + public OsuStrainSkill(Mod[] mods) : base(mods) { } @@ -22,11 +26,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double strainMultiplier; List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); - double baseLine = 0.68; - - for (int i = 0; i <= 9; i++) + for (int i = 0; i < ReducedSectionCount; i++) { - strainMultiplier = baseLine + Math.Log10(i+1) * (1.0 - baseLine); + strainMultiplier = ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); strains[i] = strains[i] * strainMultiplier; } @@ -38,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills weight *= DecayWeight; } - return difficulty * 1.06; + return difficulty * DifficultyMultiplier; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 975a633bad..0d8400badc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. /// - public class Speed : OsuSkill + public class Speed : OsuStrainSkill { private const double single_spacing_threshold = 125; From e987a511bad01745370702f7104cc955e165ce1d Mon Sep 17 00:00:00 2001 From: emu1337 Date: Mon, 14 Jun 2021 19:22:35 +0200 Subject: [PATCH 022/489] diffspike & wide angle balance --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index a95bf4fa94..e063276ec5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); - result = 1.35 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); + result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index f1c7ae3403..136c4ae309 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -10,9 +10,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { public abstract class OsuStrainSkill : StrainSkill { - protected virtual int ReducedSectionCount => 9; - protected virtual double ReducedStrainBaseline => 0.68; - protected virtual double DifficultyMultiplier => 1.06; + protected virtual int ReducedSectionCount => 10; + protected virtual double ReducedStrainBaseline => 0.7; + protected virtual double DifficultyMultiplier => 1.08; public OsuStrainSkill(Mod[] mods) : base(mods) { From 4ffff06dcbc0ac52d49d70643c5bd00d881d8c1c Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 15 Jun 2021 11:06:56 +0800 Subject: [PATCH 023/489] Break ApplyToBeatmap into subroutines --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 116 ++++++++++++--------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 0d123a4ce3..3bb4b0419f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -67,14 +67,31 @@ namespace osu.Game.Rulesets.Osu.Mods { Seed.Value ??= RNG.Next(); - var rng = new Random(Seed.Value.GetValueOrDefault()); - - float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); - var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); - // Only place circles between startTime and endTime + var hitObjects = generateBeats(osuBeatmap, origHitObjects) + .Select(x => + { + var newCircle = new HitCircle(); + newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.StartTime = x; + return (OsuHitObject)newCircle; + }).ToList(); + + addHitSamples(hitObjects, origHitObjects); + + fixComboInfo(hitObjects, origHitObjects); + + randomizeCirclePos(hitObjects); + + osuBeatmap.HitObjects = hitObjects; + + base.ApplyToBeatmap(beatmap); + } + + private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) + { var startTime = origHitObjects.First().StartTime; var endObj = origHitObjects.Last(); var endTime = endObj switch @@ -84,48 +101,45 @@ namespace osu.Game.Rulesets.Osu.Mods _ => endObj.StartTime }; - // Generate the beats - var beats = osuBeatmap.ControlPointInfo.TimingPoints - .Where(x => Precision.AlmostBigger(endTime, x.Time)) - .SelectMany(tp => - { - var tpBeats = new List(); - var currentTime = tp.Time; + var beats = beatmap.ControlPointInfo.TimingPoints + .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .SelectMany(tp => + { + var tpBeats = new List(); + var currentTime = tp.Time; - while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) - { - tpBeats.Add(currentTime); - currentTime += tp.BeatLength; - } + while (Precision.AlmostBigger(endTime, currentTime) && beatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) + { + tpBeats.Add(currentTime); + currentTime += tp.BeatLength; + } - return tpBeats; - }) - // Remove beats that are before startTime - .Where(x => Precision.AlmostBigger(x, startTime)) - // Remove beats during breaks - .Where(x => !osuBeatmap.Breaks.Any(b => - Precision.AlmostBigger(x, b.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) - )) - .ToList(); - // Generate a hit circle for each beat - var hitObjects = beats - // Remove beats that are too close to the next one (e.g. due to timing point changes) - .Where((x, idx) => - { - if (idx == beats.Count - 1) return true; + return tpBeats; + }) + .Where(x => Precision.AlmostBigger(x, startTime)) + // Remove beats during breaks + .Where(x => !beatmap.Breaks.Any(b => + Precision.AlmostBigger(x, b.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) + )) + .ToList(); - return !Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x); - }) - .Select(x => - { - var newCircle = new HitCircle(); - newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = x; - return (OsuHitObject)newCircle; - }).ToList(); + // Remove beats that are too close to the next one (e.g. due to timing point changes) + for (int i = beats.Count - 2; i >= 0; i--) + { + var beat = beats[i]; - // Add hit samples to the circles + if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + { + beats.RemoveAt(i); + } + } + + return beats; + } + + private void addHitSamples(IReadOnlyList hitObjects, IReadOnlyCollection origHitObjects) + { for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; @@ -141,8 +155,10 @@ namespace osu.Game.Rulesets.Osu.Mods x.Samples = samples; } } + } - // Process combo numbers + private void fixComboInfo(List hitObjects, List origHitObjects) + { // First follow the combo indices in the original beatmap hitObjects.ForEach(x => { @@ -165,8 +181,14 @@ namespace osu.Game.Rulesets.Osu.Mods x.IndexInCurrentCombo = j; } } + } + + private void randomizeCirclePos(IReadOnlyList hitObjects) + { + var rng = new Random(Seed.Value.GetValueOrDefault()); + + float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); - // Position all hit circles var direction = MathHelper.TwoPi * nextSingle(); for (int i = 0; i < hitObjects.Count; i++) @@ -206,10 +228,6 @@ namespace osu.Game.Rulesets.Osu.Mods direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } - - osuBeatmap.HitObjects = hitObjects; - - base.ApplyToBeatmap(beatmap); } /// From be68950c307f1ffa355b1e6fef021b27f73a36c4 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 03:34:46 +0200 Subject: [PATCH 024/489] refactoring --- .../Difficulty/OsuDifficultyCalculator.cs | 4 ++-- .../Difficulty/OsuPerformanceCalculator.cs | 9 +++++---- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 1 - .../Difficulty/Skills/OsuStrainSkill.cs | 17 +++++++++-------- .../Difficulty/Skills/Speed.cs | 1 - .../Rulesets/Difficulty/Skills/StrainSkill.cs | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index fdcc6f44a6..75d6786d95 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (beatmap.HitObjects.Count == 0) return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; - double aimRating = Math.Sqrt((skills[0] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt((skills[1] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; + double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 90ded2b837..74840853c0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -113,14 +113,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); double flashlightBonus = 1.0; + if (mods.Any(h => h is OsuModFlashlight)) { // Apply object-based bonus for flashlight. flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 - ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + - (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) - : 0.0); + (totalHits > 200 + ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + + (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) + : 0.0); } aimValue *= Math.Max(flashlightBonus, approachRateFactor); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index e063276ec5..16a18cbcb9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -3,7 +3,6 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 136c4ae309..93101bf3c3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -1,7 +1,8 @@ -using System; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; using System.Collections.Generic; -using System.Text; -using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using System.Linq; @@ -14,22 +15,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected virtual double ReducedStrainBaseline => 0.7; protected virtual double DifficultyMultiplier => 1.08; - public OsuStrainSkill(Mod[] mods) : base(mods) + protected OsuStrainSkill(Mod[] mods) + : base(mods) { } - public double OsuDifficultyValue() + public override double DifficultyValue() { double difficulty = 0; double weight = 1; - double strainMultiplier; List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < ReducedSectionCount; i++) { - strainMultiplier = ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); - strains[i] = strains[i] * strainMultiplier; + strains[i] *= ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); } // Difficulty is the weighted sum of the highest strains from every section. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 0d8400badc..872bb0601d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -3,7 +3,6 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index 71cee36812..d4fcefab9b 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// /// Returns the calculated difficulty value representing all s that have been processed up to this point. /// - public sealed override double DifficultyValue() + public override double DifficultyValue() { double difficulty = 0; double weight = 1; From 18fe05b7b5856e210fa2f91f368d2ec317bd777f Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 15:13:46 +0200 Subject: [PATCH 025/489] diffspikes balance --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 4 ++-- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 93101bf3c3..565cc5805f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -12,8 +12,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public abstract class OsuStrainSkill : StrainSkill { protected virtual int ReducedSectionCount => 10; - protected virtual double ReducedStrainBaseline => 0.7; - protected virtual double DifficultyMultiplier => 1.08; + protected virtual double ReducedStrainBaseline => 0.75; + protected virtual double DifficultyMultiplier => 1.06; protected OsuStrainSkill(Mod[] mods) : base(mods) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 872bb0601d..f0eb199e5f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 1400; protected override double StrainDecayBase => 0.3; + protected override int ReducedSectionCount => 5; + protected override double DifficultyMultiplier => 1.04; private const double min_speed_bonus = 75; // ~200BPM private const double max_speed_bonus = 45; // ~330BPM From 41662a16431eba5ddee0c75f7dbc870f1537fda1 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 19:54:22 +0200 Subject: [PATCH 026/489] refactored for clarity --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 565cc5805f..56c00d263d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using System.Linq; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -30,7 +31,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < ReducedSectionCount; i++) { - strains[i] *= ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); + strains[i] *= ReducedStrainBaseline + + Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))) + * (1.0 - ReducedStrainBaseline); } // Difficulty is the weighted sum of the highest strains from every section. From 2665a873f87e7bf2528f26507a0eb4f289b6a845 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 19:55:19 +0200 Subject: [PATCH 027/489] fixed an error with extremely short maps --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 56c00d263d..5a40524e8a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); // We are reducing the highest strains first to account for extreme difficulty spikes - for (int i = 0; i < ReducedSectionCount; i++) + for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) { strains[i] *= ReducedStrainBaseline + Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))) From 14622f473442cdf43a9539f669903fd4f9ba3f57 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 17 Jun 2021 10:20:50 +0800 Subject: [PATCH 028/489] Improved guesstimations; fixed hit samples --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3bb4b0419f..b748cdf4a2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Mods while (Precision.AlmostBigger(endTime, currentTime) && beatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) { - tpBeats.Add(currentTime); + tpBeats.Add(Math.Floor(currentTime)); currentTime += tp.BeatLength; } @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (samples == null) { if (i > 0) - x.Samples = hitObjects[i - 1].Samples; + x.Samples = hitObjects[i - 1].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } else { @@ -289,7 +289,7 @@ namespace osu.Game.Rulesets.Osu.Mods drawable.ScaleTo(0.4f) .Then().ScaleTo(1.6f, h.TimePreempt * 2); drawable.FadeTo(0.5f) - .Then().Delay(h.TimeFadeIn).FadeTo(1f); + .Then().Delay(h.TimePreempt * 2 / 3).FadeTo(1f); // remove approach circles circle.ApproachCircle.Hide(); @@ -304,6 +304,7 @@ namespace osu.Game.Rulesets.Osu.Mods { // Decrease AR to increase preempt time difficulty.ApproachRate *= 0.5f; + difficulty.CircleSize *= 0.75f; } // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. From f22beaeb5b4e63decc9fa0e0837920e6d496fbc2 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 17 Jun 2021 14:30:59 +0800 Subject: [PATCH 029/489] Increase distance between combos; pull circles closer to center --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b748cdf4a2..e91b2cd9d7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -193,15 +193,17 @@ namespace osu.Game.Rulesets.Osu.Mods for (int i = 0; i < hitObjects.Count; i++) { - var x = hitObjects[i]; + var obj = hitObjects[i]; if (i == 0) { - x.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); + obj.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); } else { - var distance = Math.Min(max_distance, 40f * (float)Math.Pow(1.05, x.ComboIndex)); + var distance = 40f * (float)Math.Pow(1.05, obj.ComboIndex); + if (obj.NewCombo) distance *= 1.5f; + distance = Math.Min(max_distance, distance); var relativePos = new Vector2( distance * (float)Math.Cos(direction), distance * (float)Math.Sin(direction) @@ -220,9 +222,9 @@ namespace osu.Game.Rulesets.Osu.Mods else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) newPosition.X = OsuPlayfield.BASE_SIZE.X; - x.Position = newPosition; + obj.Position = newPosition; - if (x.LastInCombo) + if (obj.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); else direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); @@ -354,7 +356,7 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance / 2 + relativeRotationDistance * 0.75f ); } From b7f43405fc3d9175db86cf165ced58a1a97ec057 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 17 Jun 2021 22:01:58 +0800 Subject: [PATCH 030/489] Dim circles instead of fade; improved hit samples; changed jump distance to be closer to cuttingedge --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 268 +++++++++++---------- 1 file changed, 138 insertions(+), 130 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index e91b2cd9d7..c9e43704e1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -26,6 +26,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -46,12 +47,23 @@ namespace osu.Game.Rulesets.Osu.Mods Value = null }; - public bool PerformFail() => true; - public bool RestartOnFail => false; public bool DisplayResultsOnFail => true; + // Maximum distance to jump + private const float max_distance = 250f; + + // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The closer the hit objects draw to the border, the sharper the turn + private const byte border_distance_x = 192; + private const byte border_distance_y = 144; + + public bool PerformFail() + { + return true; + } + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { // Sudden death @@ -60,9 +72,6 @@ namespace osu.Game.Rulesets.Osu.Mods && !result.IsHit; } - // Maximum distance to jump - private const float max_distance = 250f; - public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); @@ -90,6 +99,55 @@ namespace osu.Game.Rulesets.Osu.Mods base.ApplyToBeatmap(beatmap); } + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + // Decrease AR to increase preempt time + difficulty.ApproachRate *= 0.5f; + difficulty.CircleSize *= 0.75f; + } + + // Background metronome + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Overlays.Add(new TargetBeatContainer()); + } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableHitCircle circle)) return; + + var h = (OsuHitObject)drawable.HitObject; + + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + drawable.ScaleTo(0.5f) + .Then().ScaleTo(1f, h.TimePreempt); + + var colour = drawable.Colour; + + var avgColour = colour.AverageColour.Linear; + drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) + .Then().Delay(h.TimeFadeIn).FadeColour(colour); + + // remove approach circles + circle.ApproachCircle.Hide(); + } + } + + private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) + { + return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; + } + private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) { var startTime = origHitObjects.First().StartTime; @@ -125,35 +183,43 @@ namespace osu.Game.Rulesets.Osu.Mods .ToList(); // Remove beats that are too close to the next one (e.g. due to timing point changes) - for (int i = beats.Count - 2; i >= 0; i--) + for (var i = beats.Count - 2; i >= 0; i--) { var beat = beats[i]; - if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) - { - beats.RemoveAt(i); - } + if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) beats.RemoveAt(i); } return beats; } - private void addHitSamples(IReadOnlyList hitObjects, IReadOnlyCollection origHitObjects) + private void addHitSamples(IEnumerable hitObjects, IReadOnlyList origHitObjects) { - for (int i = 0; i < hitObjects.Count; i++) + var lastSampleIdx = 0; + + foreach (var x in hitObjects) { - var x = hitObjects[i]; var samples = getSamplesAtTime(origHitObjects, x.StartTime); if (samples == null) { - if (i > 0) - x.Samples = hitObjects[i - 1].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); + while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= x.StartTime) + lastSampleIdx++; + lastSampleIdx--; + + if (lastSampleIdx < 0 && lastSampleIdx >= origHitObjects.Count) continue; + + if (lastSampleIdx < origHitObjects.Count - 1) + { + // get samples from the next hit object if it is closer in time + if (origHitObjects[lastSampleIdx + 1].StartTime - x.StartTime < x.StartTime - origHitObjects[lastSampleIdx].StartTime) + lastSampleIdx++; + } + + x.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } else - { x.Samples = samples; - } } } @@ -168,13 +234,13 @@ namespace osu.Game.Rulesets.Osu.Mods // Then reprocess them to ensure continuity in the combo indices and add indices in current combo var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); - for (int i = 0; i < combos.Count; i++) + for (var i = 0; i < combos.Count; i++) { var group = combos[i].ToList(); group.First().NewCombo = true; group.Last().LastInCombo = true; - for (int j = 0; j < group.Count; j++) + for (var j = 0; j < group.Count; j++) { var x = group[j]; x.ComboIndex = i; @@ -190,53 +256,52 @@ namespace osu.Game.Rulesets.Osu.Mods float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); var direction = MathHelper.TwoPi * nextSingle(); + var maxComboIndex = hitObjects.Last().ComboIndex; - for (int i = 0; i < hitObjects.Count; i++) + for (var i = 0; i < hitObjects.Count; i++) { var obj = hitObjects[i]; + var lastPos = i == 0 + ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2) + : hitObjects[i - 1].Position; - if (i == 0) - { - obj.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); - } + var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, 333f); + if (obj.NewCombo) distance *= 1.5f; + if (obj.Kiai) distance *= 1.2f; + distance = Math.Min(max_distance, distance); + + var relativePos = new Vector2( + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); + relativePos = getRotatedVector(lastPos, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + + var newPosition = Vector2.Add(lastPos, relativePos); + + if (newPosition.Y < 0) + newPosition.Y = 0; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y; + if (newPosition.X < 0) + newPosition.X = 0; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) + newPosition.X = OsuPlayfield.BASE_SIZE.X; + + obj.Position = newPosition; + + if (obj.LastInCombo) + direction = MathHelper.TwoPi * nextSingle(); else - { - var distance = 40f * (float)Math.Pow(1.05, obj.ComboIndex); - if (obj.NewCombo) distance *= 1.5f; - distance = Math.Min(max_distance, distance); - var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) - ); - relativePos = getRotatedVector(hitObjects[i - 1].Position, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); - - var newPosition = Vector2.Add(hitObjects[i - 1].Position, relativePos); - - if (newPosition.Y < 0) - newPosition.Y = 0; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y; - if (newPosition.X < 0) - newPosition.X = 0; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) - newPosition.X = OsuPlayfield.BASE_SIZE.X; - - obj.Position = newPosition; - - if (obj.LastInCombo) - direction = MathHelper.TwoPi * nextSingle(); - else - direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); - } + direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } /// - /// Get samples (if any) for a specific point in time. + /// Get samples (if any) for a specific point in time. /// /// - /// Samples will be returned if a hit circle or a slider node exists at that point of time. + /// Samples will be returned if a hit circle or a slider node exists at that point of time. /// /// The list of hit objects in a beatmap, ordered by StartTime /// The point in time to get samples for @@ -260,62 +325,15 @@ namespace osu.Game.Rulesets.Osu.Mods IList samples; if (sampleObj is Slider slider) - { samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; - } else - { samples = sampleObj.Samples; - } return samples; } - protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) - { - } - - protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) - { - if (drawable is DrawableSpinner) - return; - - var h = (OsuHitObject)drawable.HitObject; - - // apply grow and fade effect - if (!(drawable is DrawableHitCircle circle)) return; - - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - // todo: this doesn't feel quite right yet - drawable.ScaleTo(0.4f) - .Then().ScaleTo(1.6f, h.TimePreempt * 2); - drawable.FadeTo(0.5f) - .Then().Delay(h.TimePreempt * 2 / 3).FadeTo(1f); - - // remove approach circles - circle.ApproachCircle.Hide(); - } - } - - public void ReadFromDifficulty(BeatmapDifficulty difficulty) - { - } - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - // Decrease AR to increase preempt time - difficulty.ApproachRate *= 0.5f; - difficulty.CircleSize *= 0.75f; - } - - // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. - // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 192; - private const byte border_distance_y = 144; - /// - /// Determines the position of the current hit object relative to the previous one. + /// Determines the position of the current hit object relative to the previous one. /// /// The position of the current hit object relative to the previous one private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) @@ -356,16 +374,19 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance * 0.75f + Math.Min(1, relativeRotationDistance * 0.75f) ); } /// - /// Rotates vector "initial" towards vector "destination" + /// Rotates vector "initial" towards vector "destination" /// /// Vector to rotate to "destination" /// Vector "initial" should be rotated to - /// The angle the vector should be rotated relative to the difference between the angles of the the two vectors. + /// + /// The angle the vector should be rotated relative to the difference between the angles of + /// the the two vectors. + /// /// Resulting vector private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) { @@ -374,15 +395,9 @@ namespace osu.Game.Rulesets.Osu.Mods var diff = destAngleRad - initialAngleRad; - while (diff < -Math.PI) - { - diff += 2 * Math.PI; - } + while (diff < -Math.PI) diff += 2 * Math.PI; - while (diff > Math.PI) - { - diff -= 2 * Math.PI; - } + while (diff > Math.PI) diff -= 2 * Math.PI; var finalAngleRad = initialAngleRad + relativeDistance * diff; @@ -392,13 +407,6 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - // Background metronome - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - drawableRuleset.Overlays.Add(new TargetBeatContainer()); - } - public class TargetBeatContainer : BeatSyncedContainer { private PausableSkinnableSound sample; @@ -408,15 +416,6 @@ namespace osu.Game.Rulesets.Osu.Mods Divisor = 1; } - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample? - }; - } - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); @@ -425,6 +424,15 @@ namespace osu.Game.Rulesets.Osu.Mods sample?.Play(); } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample + }; + } } } } From 8c4e60e5ccca84571b0aa5494e665519e7d652b7 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Thu, 17 Jun 2021 21:41:06 +0200 Subject: [PATCH 031/489] xmldoc and refactoring --- .../Difficulty/Skills/OsuStrainSkill.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 5a40524e8a..e47edc37cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -12,8 +12,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { public abstract class OsuStrainSkill : StrainSkill { + /// + /// The number of sections with the highest strains, which the peak strain reductions will apply to. + /// This is done in order to decrease their impact on the overall difficulty of the map for this skill. + /// protected virtual int ReducedSectionCount => 10; + + /// + /// The baseline multiplier applied to the section with the biggest strain. + /// protected virtual double ReducedStrainBaseline => 0.75; + + /// + /// The final multiplier to be applied to after all other calculations. + /// protected virtual double DifficultyMultiplier => 1.06; protected OsuStrainSkill(Mod[] mods) @@ -31,9 +43,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) { - strains[i] *= ReducedStrainBaseline - + Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))) - * (1.0 - ReducedStrainBaseline); + double scale = Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))); + strains[i] *= Interpolation.Lerp(ReducedStrainBaseline, 1.0, scale); } // Difficulty is the weighted sum of the highest strains from every section. From 639e8b62b94c37cadb3d9bf84da159efa20a9e36 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 11:20:04 +0800 Subject: [PATCH 032/489] Make circles light up 1 beat length before start time --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index c9e43704e1..db6d483dee 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -59,6 +59,8 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 192; private const byte border_distance_y = 144; + private ControlPointInfo controlPointInfo; + public bool PerformFail() { return true; @@ -77,13 +79,14 @@ namespace osu.Game.Rulesets.Osu.Mods Seed.Value ??= RNG.Next(); var osuBeatmap = (OsuBeatmap)beatmap; + controlPointInfo = osuBeatmap.ControlPointInfo; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); var hitObjects = generateBeats(osuBeatmap, origHitObjects) .Select(x => { var newCircle = new HitCircle(); - newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.ApplyDefaults(controlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); newCircle.StartTime = x; return (OsuHitObject)newCircle; }).ToList(); @@ -136,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Mods var avgColour = colour.AverageColour.Linear; drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimeFadeIn).FadeColour(colour); + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength).FadeColour(colour); // remove approach circles circle.ApproachCircle.Hide(); From dca2d8af4f7c2027f4f874237cc6aadf809cc16c Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 13:18:44 +0800 Subject: [PATCH 033/489] Animate circles undimming --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index db6d483dee..b7463103df 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Mods var avgColour = colour.AverageColour.Linear; drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength).FadeColour(colour); + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - 96).FadeColour(colour, 96); // remove approach circles circle.ApproachCircle.Hide(); From f5134c7fc279d804067ecd8d30b291f654994a02 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 14:39:46 +0800 Subject: [PATCH 034/489] Extract constants and add xmldoc --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 25 ++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b7463103df..63fda42393 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -51,14 +51,26 @@ namespace osu.Game.Rulesets.Osu.Mods public bool DisplayResultsOnFail => true; - // Maximum distance to jump - private const float max_distance = 250f; + /// + /// Jump distance for circles in the last combo + /// + private const float max_base_distance = 250f; + + /// + /// The maximum allowed jump distance after multipliers are applied + /// + private const float distance_cap = 350f; // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn private const byte border_distance_x = 192; private const byte border_distance_y = 144; + /// + /// Duration of the undimming animation + /// + private const double undim_duration = 96; + private ControlPointInfo controlPointInfo; public bool PerformFail() @@ -139,7 +151,8 @@ namespace osu.Game.Rulesets.Osu.Mods var avgColour = colour.AverageColour.Linear; drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - 96).FadeColour(colour, 96); + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) + .FadeColour(colour, undim_duration); // remove approach circles circle.ApproachCircle.Hide(); @@ -268,10 +281,10 @@ namespace osu.Game.Rulesets.Osu.Mods ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2) : hitObjects[i - 1].Position; - var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, 333f); + var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); if (obj.NewCombo) distance *= 1.5f; if (obj.Kiai) distance *= 1.2f; - distance = Math.Min(max_distance, distance); + distance = Math.Min(distance_cap, distance); var relativePos = new Vector2( distance * (float)Math.Cos(direction), @@ -296,7 +309,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (obj.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); else - direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / distance_cap * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } From cca26d465110153254eeac9e2fbd078a4f771e5e Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 16:05:09 +0800 Subject: [PATCH 035/489] Take circle radius into account when clamping hit objects to playfield --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 43 +++++++++++++--------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 63fda42393..de7309c27f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -52,12 +52,12 @@ namespace osu.Game.Rulesets.Osu.Mods public bool DisplayResultsOnFail => true; /// - /// Jump distance for circles in the last combo + /// Jump distance for circles in the last combo /// private const float max_base_distance = 250f; /// - /// The maximum allowed jump distance after multipliers are applied + /// The maximum allowed jump distance after multipliers are applied /// private const float distance_cap = 350f; @@ -67,7 +67,12 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_y = 144; /// - /// Duration of the undimming animation + /// The extent of rotation towards playfield centre when a circle is near the edge + /// + private const float edge_rotation_multiplier = 0.75f; + + /// + /// Duration of the undimming animation /// private const double undim_duration = 96; @@ -295,14 +300,16 @@ namespace osu.Game.Rulesets.Osu.Mods var newPosition = Vector2.Add(lastPos, relativePos); - if (newPosition.Y < 0) - newPosition.Y = 0; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y; - if (newPosition.X < 0) - newPosition.X = 0; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) - newPosition.X = OsuPlayfield.BASE_SIZE.X; + var radius = (float)obj.Radius; + + if (newPosition.Y < radius) + newPosition.Y = radius; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; + if (newPosition.X < radius) + newPosition.X = radius; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) + newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; obj.Position = newPosition; @@ -314,10 +321,10 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Get samples (if any) for a specific point in time. + /// Get samples (if any) for a specific point in time. /// /// - /// Samples will be returned if a hit circle or a slider node exists at that point of time. + /// Samples will be returned if a hit circle or a slider node exists at that point of time. /// /// The list of hit objects in a beatmap, ordered by StartTime /// The point in time to get samples for @@ -349,7 +356,7 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Determines the position of the current hit object relative to the previous one. + /// Determines the position of the current hit object relative to the previous one. /// /// The position of the current hit object relative to the previous one private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) @@ -390,18 +397,18 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - Math.Min(1, relativeRotationDistance * 0.75f) + Math.Min(1, relativeRotationDistance * edge_rotation_multiplier) ); } /// - /// Rotates vector "initial" towards vector "destination" + /// Rotates vector "initial" towards vector "destination" /// /// Vector to rotate to "destination" /// Vector "initial" should be rotated to /// - /// The angle the vector should be rotated relative to the difference between the angles of - /// the the two vectors. + /// The angle the vector should be rotated relative to the difference between the angles of + /// the the two vectors. /// /// Resulting vector private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) From 403909b598e4c735a1a1e47c429593eb54164b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 18 Jun 2021 12:49:49 +0200 Subject: [PATCH 036/489] Update SR test values in line with diffspike changes --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index afd94f4570..8d8387378e 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.9311451172574934d, "diffcalc-test")] - [TestCase(1.0736586907780401d, "zero-length-sliders")] + [TestCase(6.7568168283591499d, "diffcalc-test")] + [TestCase(1.0348244046058293d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.7212283220412345d, "diffcalc-test")] - [TestCase(1.3212137158641493d, "zero-length-sliders")] + [TestCase(8.4783236764532557d, "diffcalc-test")] + [TestCase(1.2708532136987165d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From ca8f08ca84894258478ff86a283aa8729986c433 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 10:04:48 +0800 Subject: [PATCH 037/489] Avoid overlapping with recent circles --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 62 +++++++++++++++------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index de7309c27f..4e4e798f47 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -54,12 +54,12 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Jump distance for circles in the last combo /// - private const float max_base_distance = 250f; + private const float max_base_distance = 333f; /// /// The maximum allowed jump distance after multipliers are applied /// - private const float distance_cap = 350f; + private const float distance_cap = 380f; // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn @@ -71,6 +71,11 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const float edge_rotation_multiplier = 0.75f; + /// + /// Number of recent circles to check for overlap + /// + private const int overlap_check_count = 5; + /// /// Duration of the undimming animation /// @@ -291,27 +296,39 @@ namespace osu.Game.Rulesets.Osu.Mods if (obj.Kiai) distance *= 1.2f; distance = Math.Min(distance_cap, distance); - var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) - ); - relativePos = getRotatedVector(lastPos, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + // Attempt to place the circle at a place that does not overlap with previous ones - var newPosition = Vector2.Add(lastPos, relativePos); + var tryCount = 0; - var radius = (float)obj.Radius; + do + { + if (tryCount > 0) direction = MathHelper.TwoPi * nextSingle(); - if (newPosition.Y < radius) - newPosition.Y = radius; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; - if (newPosition.X < radius) - newPosition.X = radius; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) - newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; + var relativePos = new Vector2( + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); + relativePos = getRotatedVector(lastPos, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); - obj.Position = newPosition; + var newPosition = Vector2.Add(lastPos, relativePos); + + var radius = (float)obj.Radius; + + if (newPosition.Y < radius) + newPosition.Y = radius; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; + if (newPosition.X < radius) + newPosition.X = radius; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) + newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; + + obj.Position = newPosition; + + tryCount++; + if (tryCount % 10 == 0) distance *= 0.9f; + } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); if (obj.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); @@ -355,6 +372,13 @@ namespace osu.Game.Rulesets.Osu.Mods return samples; } + private bool isOverlappingWithRecent(IReadOnlyList hitObjects, int idx) + { + var target = hitObjects[idx]; + return hitObjects.SkipLast(hitObjects.Count - idx).TakeLast(overlap_check_count) + .Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); + } + /// /// Determines the position of the current hit object relative to the previous one. /// From 0cf3119006f8a0a9ec8d4513e646ca4705320bcf Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 11:12:29 +0800 Subject: [PATCH 038/489] Guard against edge cases --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 4e4e798f47..0e7dc7a9d3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -101,6 +101,9 @@ namespace osu.Game.Rulesets.Osu.Mods Seed.Value ??= RNG.Next(); var osuBeatmap = (OsuBeatmap)beatmap; + + if (osuBeatmap.HitObjects.Count == 0) return; + controlPointInfo = osuBeatmap.ControlPointInfo; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); @@ -277,6 +280,8 @@ namespace osu.Game.Rulesets.Osu.Mods private void randomizeCirclePos(IReadOnlyList hitObjects) { + if (hitObjects.Count == 0) return; + var rng = new Random(Seed.Value.GetValueOrDefault()); float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); @@ -291,7 +296,9 @@ namespace osu.Game.Rulesets.Osu.Mods ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2) : hitObjects[i - 1].Position; - var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); + var distance = maxComboIndex == 0 + ? (float)obj.Radius + : map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); if (obj.NewCombo) distance *= 1.5f; if (obj.Kiai) distance *= 1.2f; distance = Math.Min(distance_cap, distance); From b09165a074c11661f656836f946c6a5ddd1f13a0 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 11:13:19 +0800 Subject: [PATCH 039/489] Remove the circle size buff --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 0e7dc7a9d3..9688bc7154 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -135,7 +135,6 @@ namespace osu.Game.Rulesets.Osu.Mods { // Decrease AR to increase preempt time difficulty.ApproachRate *= 0.5f; - difficulty.CircleSize *= 0.75f; } // Background metronome From c86794058442186d219391fdcf05d396c8651775 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 12:26:16 +0800 Subject: [PATCH 040/489] Marked target mod and traceable mod as incompatible; extracted playfield clamping logic Nothing is visible when target mod and traceable mod are enabled together. --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 33 ++++++++++++------- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 2 +- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 9688bc7154..a6476541ab 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable { @@ -319,19 +321,10 @@ namespace osu.Game.Rulesets.Osu.Mods var newPosition = Vector2.Add(lastPos, relativePos); - var radius = (float)obj.Radius; - - if (newPosition.Y < radius) - newPosition.Y = radius; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; - if (newPosition.X < radius) - newPosition.X = radius; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) - newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; - obj.Position = newPosition; + clampToPlayfield(obj); + tryCount++; if (tryCount % 10 == 0) distance *= 0.9f; } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); @@ -460,6 +453,24 @@ namespace osu.Game.Rulesets.Osu.Mods ); } + private void clampToPlayfield(OsuHitObject obj) + { + var position = obj.Position; + var radius = (float)obj.Radius; + + if (position.Y < radius) + position.Y = radius; + else if (position.Y > OsuPlayfield.BASE_SIZE.Y - radius) + position.Y = OsuPlayfield.BASE_SIZE.Y - radius; + + if (position.X < radius) + position.X = radius; + else if (position.X > OsuPlayfield.BASE_SIZE.X - radius) + position.X = OsuPlayfield.BASE_SIZE.X - radius; + + obj.Position = position; + } + public class TargetBeatContainer : BeatSyncedContainer { private PausableSkinnableSound sample; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 4b0939db16..c5b0a2da3e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween), typeof(OsuModTarget) }; protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { From e52a58c1bc5f17079ee3fe7fd85b10b2953ff3c0 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 21 Jun 2021 16:24:37 +0800 Subject: [PATCH 041/489] Switched to a more reasonable sample sound for now --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index a6476541ab..75089fe237 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -494,7 +494,7 @@ namespace osu.Game.Rulesets.Osu.Mods { InternalChildren = new Drawable[] { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample }; } } From 5a031eada861095340b1d9c7ae428b9318f240a7 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 23 Jun 2021 16:22:10 +0800 Subject: [PATCH 042/489] Revert "Display results after fail" This commit reverts 7815b3c7 --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 -- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 -- osu.Game/Rulesets/Mods/IApplicableFailOverride.cs | 5 ----- osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 -- osu.Game/Rulesets/Mods/ModBlockFail.cs | 1 - osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs | 1 - osu.Game/Rulesets/Mods/ModFailCondition.cs | 1 - osu.Game/Screens/Play/Player.cs | 10 ++-------- 8 files changed, 2 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 2b92174b29..aac830801b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Osu.Mods public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; - private OsuInputManager inputManager; private IFrameStableClock gameplayClock; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 75089fe237..bf31839991 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -51,8 +51,6 @@ namespace osu.Game.Rulesets.Osu.Mods public bool RestartOnFail => false; - public bool DisplayResultsOnFail => true; - /// /// Jump distance for circles in the last combo /// diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index b7d306e57b..8c99d739cb 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -18,10 +18,5 @@ namespace osu.Game.Rulesets.Mods /// Whether we want to restart on fail. Only used if returns true. /// bool RestartOnFail { get; } - - /// - /// Whether to proceed to results screen on fail. Only used if returns true and is false. - /// - bool DisplayResultsOnFail { get; } } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index e5652a8609..b84b5671e1 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; - public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) }; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 973a192378..1fde5abad4 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Mods public bool PerformFail() => false; public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; public void ReadFromConfig(OsuConfigManager config) { diff --git a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs index 91508b7e70..2ac0f59d84 100644 --- a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs +++ b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs @@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Mods } public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index 012a0f73ff..c0d7bae2b2 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; public virtual bool RestartOnFail => true; - public virtual bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4726d54ebc..f9036780aa 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -640,7 +640,7 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - if (!ScoreProcessor.HasCompleted.Value && !HealthProcessor.HasFailed) + if (!ScoreProcessor.HasCompleted.Value) { completionProgressDelegate?.Cancel(); completionProgressDelegate = null; @@ -653,7 +653,7 @@ namespace osu.Game.Screens.Play throw new InvalidOperationException($"{nameof(updateCompletionState)} was fired more than once"); // Only show the completion screen if the player hasn't failed - if (HealthProcessor.HasFailed && !Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) + if (HealthProcessor.HasFailed) return; ValidForResume = false; @@ -751,12 +751,6 @@ namespace osu.Game.Screens.Play // Called back when the transform finishes private void onFailComplete() { - if (Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) - { - updateCompletionState(true); - return; - } - GameplayClockContainer.Stop(); FailOverlay.Retries = RestartCount; From 6dc5f406b27818051aa6423b8f7e1187414408df Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 23 Jun 2021 16:29:36 +0800 Subject: [PATCH 043/489] Implement IMutateApproachCircles --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index bf31839991..559ad31751 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -31,7 +31,8 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, + IHasSeed, IMutateApproachCircles { public override string Name => "Target"; public override string Acronym => "TP"; @@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; + public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable From b7dd26612d2ca78fe09a38980e3f1c0b9c472cd7 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 23 Jun 2021 16:50:05 +0800 Subject: [PATCH 044/489] Reordered things and added regions --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 185 ++++++++++++--------- 1 file changed, 106 insertions(+), 79 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 559ad31751..6057b134e3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods Value = null }; - public bool RestartOnFail => false; + #region Constants /// /// Jump distance for circles in the last combo @@ -82,12 +82,19 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const double undim_duration = 96; + #endregion + + #region Private Fields + private ControlPointInfo controlPointInfo; - public bool PerformFail() - { - return true; - } + #endregion + + #region Sudden Death (IApplicableFailOverride) + + public bool PerformFail() => true; + + public bool RestartOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { @@ -97,6 +104,55 @@ namespace osu.Game.Rulesets.Osu.Mods && !result.IsHit; } + #endregion + + #region Reduce AR (IApplicableToDifficulty) + + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + // Decrease AR to increase preempt time + difficulty.ApproachRate *= 0.5f; + } + + #endregion + + #region Circle Transforms (ModWithVisibilityAdjustment) + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableHitCircle circle)) return; + + var h = (OsuHitObject)drawable.HitObject; + + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + drawable.ScaleTo(0.5f) + .Then().ScaleTo(1f, h.TimePreempt); + + var colour = drawable.Colour; + + var avgColour = colour.AverageColour.Linear; + drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) + .FadeColour(colour, undim_duration); + + // remove approach circles + circle.ApproachCircle.Hide(); + } + } + + #endregion + + #region Beatmap Generation (IApplicableToBeatmap) + public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); @@ -128,55 +184,6 @@ namespace osu.Game.Rulesets.Osu.Mods base.ApplyToBeatmap(beatmap); } - public void ReadFromDifficulty(BeatmapDifficulty difficulty) - { - } - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - // Decrease AR to increase preempt time - difficulty.ApproachRate *= 0.5f; - } - - // Background metronome - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - drawableRuleset.Overlays.Add(new TargetBeatContainer()); - } - - protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) - { - } - - protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) - { - if (!(drawable is DrawableHitCircle circle)) return; - - var h = (OsuHitObject)drawable.HitObject; - - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - drawable.ScaleTo(0.5f) - .Then().ScaleTo(1f, h.TimePreempt); - - var colour = drawable.Colour; - - var avgColour = colour.AverageColour.Linear; - drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) - .FadeColour(colour, undim_duration); - - // remove approach circles - circle.ApproachCircle.Hide(); - } - } - - private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) - { - return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; - } - private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) { var startTime = origHitObjects.First().StartTime; @@ -335,6 +342,47 @@ namespace osu.Game.Rulesets.Osu.Mods } } + #endregion + + #region Metronome (IApplicableToDrawableRuleset) + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Overlays.Add(new TargetBeatContainer()); + } + + public class TargetBeatContainer : BeatSyncedContainer + { + private PausableSkinnableSound sample; + + public TargetBeatContainer() + { + Divisor = 1; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + sample?.Play(); + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample + }; + } + } + + #endregion + + #region Helper Subroutines + /// /// Get samples (if any) for a specific point in time. /// @@ -470,32 +518,11 @@ namespace osu.Game.Rulesets.Osu.Mods obj.Position = position; } - public class TargetBeatContainer : BeatSyncedContainer + private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) { - private PausableSkinnableSound sample; - - public TargetBeatContainer() - { - Divisor = 1; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - if (!IsBeatSyncedWithTrack) return; - - sample?.Play(); - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample - }; - } + return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } + + #endregion } } From a7ea7b8b0be060f50ba4e20044aa220e79229037 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 09:34:39 +0800 Subject: [PATCH 045/489] Use `GetEndTime()` instead of a switch expression --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 6057b134e3..834876b455 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -17,6 +17,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; @@ -188,12 +189,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var startTime = origHitObjects.First().StartTime; var endObj = origHitObjects.Last(); - var endTime = endObj switch - { - Slider slider => slider.EndTime, - Spinner spinner => spinner.EndTime, - _ => endObj.StartTime - }; + var endTime = endObj.GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints .Where(x => Precision.AlmostBigger(endTime, x.Time)) From dae7b8025da3a867e449fe3025954dbacac449b3 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 09:51:45 +0800 Subject: [PATCH 046/489] Converted an inline lambda into a method (`getBeatsForTimingPoint`) --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 834876b455..130489d4c8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -193,19 +193,7 @@ namespace osu.Game.Rulesets.Osu.Mods var beats = beatmap.ControlPointInfo.TimingPoints .Where(x => Precision.AlmostBigger(endTime, x.Time)) - .SelectMany(tp => - { - var tpBeats = new List(); - var currentTime = tp.Time; - - while (Precision.AlmostBigger(endTime, currentTime) && beatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) - { - tpBeats.Add(Math.Floor(currentTime)); - currentTime += tp.BeatLength; - } - - return tpBeats; - }) + .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) .Where(x => Precision.AlmostBigger(x, startTime)) // Remove beats during breaks .Where(x => !beatmap.Breaks.Any(b => @@ -379,6 +367,22 @@ namespace osu.Game.Rulesets.Osu.Mods #region Helper Subroutines + private IEnumerable getBeatsForTimingPoint(TimingControlPoint timingPoint, double mapEndTime) + { + var beats = new List(); + int i = 0; + var currentTime = timingPoint.Time; + + while (Precision.AlmostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) + { + beats.Add(Math.Floor(currentTime)); + i++; + currentTime = timingPoint.Time + i * timingPoint.BeatLength; + } + + return beats; + } + /// /// Get samples (if any) for a specific point in time. /// From 3eab540bcca713c73d73f052a4e0f6656b78f1fb Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:07:29 +0800 Subject: [PATCH 047/489] Converted an inline lambda into a method (`isInsideBreakPeriod`); moved `origHitObjects` to be a private class field --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 47 +++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 130489d4c8..7a194f4a98 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -13,6 +13,7 @@ using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -89,6 +90,8 @@ namespace osu.Game.Rulesets.Osu.Mods private ControlPointInfo controlPointInfo; + private List origHitObjects; + #endregion #region Sudden Death (IApplicableFailOverride) @@ -163,9 +166,9 @@ namespace osu.Game.Rulesets.Osu.Mods if (osuBeatmap.HitObjects.Count == 0) return; controlPointInfo = osuBeatmap.ControlPointInfo; - var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); + origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); - var hitObjects = generateBeats(osuBeatmap, origHitObjects) + var hitObjects = generateBeats(osuBeatmap) .Select(x => { var newCircle = new HitCircle(); @@ -174,9 +177,9 @@ namespace osu.Game.Rulesets.Osu.Mods return (OsuHitObject)newCircle; }).ToList(); - addHitSamples(hitObjects, origHitObjects); + addHitSamples(hitObjects); - fixComboInfo(hitObjects, origHitObjects); + fixComboInfo(hitObjects); randomizeCirclePos(hitObjects); @@ -185,21 +188,17 @@ namespace osu.Game.Rulesets.Osu.Mods base.ApplyToBeatmap(beatmap); } - private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) + private IEnumerable generateBeats(IBeatmap beatmap) { var startTime = origHitObjects.First().StartTime; var endObj = origHitObjects.Last(); var endTime = endObj.GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints - .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) - .Where(x => Precision.AlmostBigger(x, startTime)) - // Remove beats during breaks - .Where(x => !beatmap.Breaks.Any(b => - Precision.AlmostBigger(x, b.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) - )) + .Where(beat => Precision.AlmostBigger(beat, startTime)) + .Where(beat => isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); // Remove beats that are too close to the next one (e.g. due to timing point changes) @@ -213,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Mods return beats; } - private void addHitSamples(IEnumerable hitObjects, IReadOnlyList origHitObjects) + private void addHitSamples(IEnumerable hitObjects) { var lastSampleIdx = 0; @@ -243,7 +242,7 @@ namespace osu.Game.Rulesets.Osu.Mods } } - private void fixComboInfo(List hitObjects, List origHitObjects) + private void fixComboInfo(List hitObjects) { // First follow the combo indices in the original beatmap hitObjects.ForEach(x => @@ -367,6 +366,26 @@ namespace osu.Game.Rulesets.Osu.Mods #region Helper Subroutines + /// + /// Check if a given time is inside a . + /// + /// + /// The given time is also considered to be inside a break if it is earlier than the + /// start time of the first original hit object after the break. + /// + /// The breaks of the beatmap. + /// The time to be checked.= + private bool isInsideBreakPeriod(IEnumerable breaks, double time) + { + return !breaks.Any(breakPeriod => + { + var firstObjAfterBreak = origHitObjects.First(obj => Precision.AlmostBigger(obj.StartTime, breakPeriod.EndTime)); + + return Precision.AlmostBigger(time, breakPeriod.StartTime) + && Precision.AlmostBigger(firstObjAfterBreak.StartTime, time); + }); + } + private IEnumerable getBeatsForTimingPoint(TimingControlPoint timingPoint, double mapEndTime) { var beats = new List(); From 98003ec548f42a8a834ba42ae5c52d03abc24c35 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:33:54 +0800 Subject: [PATCH 048/489] Avoid modulo when finding slider node index --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 31 +++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 7a194f4a98..31fb70e9c0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -413,30 +414,48 @@ namespace osu.Game.Rulesets.Osu.Mods /// Hit samples private IList getSamplesAtTime(IEnumerable hitObjects, double time) { - var sampleObj = hitObjects.FirstOrDefault(x => + var sampleObj = hitObjects.FirstOrDefault(hitObject => { - if (Precision.AlmostEquals(time, x.StartTime)) return true; + if (Precision.AlmostEquals(time, hitObject.StartTime)) + return true; - if (!(x is Slider s)) + if (!(hitObject is IHasPathWithRepeats s)) return false; - if (!Precision.AlmostBigger(time, s.StartTime) + if (!Precision.AlmostBigger(time, hitObject.StartTime) || !Precision.AlmostBigger(s.EndTime, time)) return false; - return Precision.AlmostEquals((time - s.StartTime) % s.SpanDuration, 0); + return nodeIndexFromTime(s, time - hitObject.StartTime) != -1; }); if (sampleObj == null) return null; IList samples; if (sampleObj is Slider slider) - samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; + samples = slider.NodeSamples[nodeIndexFromTime(slider, time - slider.StartTime)]; else samples = sampleObj.Samples; return samples; } + /// + /// Get the repeat node at a point in time. + /// + /// The slider. + /// The time since the start time of the slider. + /// Index of the node. -1 if there isn't a node at the specific time. + private int nodeIndexFromTime(IHasPathWithRepeats curve, double timeSinceStart) + { + double spanDuration = curve.Duration / curve.SpanCount(); + double nodeIndex = timeSinceStart / spanDuration; + + if (Precision.AlmostEquals(nodeIndex - Math.Round(nodeIndex), 0)) + return (int)Math.Round(nodeIndex); + + return -1; + } + private bool isOverlappingWithRecent(IReadOnlyList hitObjects, int idx) { var target = hitObjects[idx]; From 58b439b728c962c858f1cacbd817d6b5cb4020b4 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:38:21 +0800 Subject: [PATCH 049/489] Switch to `IHasPathWithRepeats` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 31fb70e9c0..54e6708bb8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -431,8 +431,8 @@ namespace osu.Game.Rulesets.Osu.Mods IList samples; - if (sampleObj is Slider slider) - samples = slider.NodeSamples[nodeIndexFromTime(slider, time - slider.StartTime)]; + if (sampleObj is IHasPathWithRepeats slider) + samples = slider.NodeSamples[nodeIndexFromTime(slider, time - sampleObj.StartTime)]; else samples = sampleObj.Samples; From 04510f1acee717847765b22edbba7dc2b63e416d Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:54:21 +0800 Subject: [PATCH 050/489] Removed odd-looking decrement and checks in `addHitSamples` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 54e6708bb8..f2b70b19a8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -217,29 +217,32 @@ namespace osu.Game.Rulesets.Osu.Mods { var lastSampleIdx = 0; - foreach (var x in hitObjects) + foreach (var obj in hitObjects) { - var samples = getSamplesAtTime(origHitObjects, x.StartTime); + var samples = getSamplesAtTime(origHitObjects, obj.StartTime); if (samples == null) { - while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= x.StartTime) + // If samples aren't available at the exact start time of the object, + // use samples (without additions) in the closest original hit object instead + + while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= obj.StartTime) lastSampleIdx++; - lastSampleIdx--; - if (lastSampleIdx < 0 && lastSampleIdx >= origHitObjects.Count) continue; + if (lastSampleIdx >= origHitObjects.Count) continue; - if (lastSampleIdx < origHitObjects.Count - 1) + if (lastSampleIdx > 0) { - // get samples from the next hit object if it is closer in time - if (origHitObjects[lastSampleIdx + 1].StartTime - x.StartTime < x.StartTime - origHitObjects[lastSampleIdx].StartTime) - lastSampleIdx++; + // get samples from the previous hit object if it is closer in time + if (obj.StartTime - origHitObjects[lastSampleIdx - 1].StartTime < origHitObjects[lastSampleIdx].StartTime - obj.StartTime) + lastSampleIdx--; } - x.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); + // Remove additions + obj.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } else - x.Samples = samples; + obj.Samples = samples; } } From 6202eed5e24d903e18834efb2801aa4a5d2e42b9 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:56:14 +0800 Subject: [PATCH 051/489] Moved a misplaced `!` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index f2b70b19a8..796abf30f4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -199,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Mods .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) .Where(beat => Precision.AlmostBigger(beat, startTime)) - .Where(beat => isInsideBreakPeriod(beatmap.Breaks, beat)) + .Where(beat => !isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); // Remove beats that are too close to the next one (e.g. due to timing point changes) @@ -207,7 +207,8 @@ namespace osu.Game.Rulesets.Osu.Mods { var beat = beats[i]; - if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) beats.RemoveAt(i); + if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + beats.RemoveAt(i); } return beats; @@ -381,7 +382,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// The time to be checked.= private bool isInsideBreakPeriod(IEnumerable breaks, double time) { - return !breaks.Any(breakPeriod => + return breaks.Any(breakPeriod => { var firstObjAfterBreak = origHitObjects.First(obj => Precision.AlmostBigger(obj.StartTime, breakPeriod.EndTime)); From 6fca8ba5b07a55ebc97326812ee1548f3c0be9ab Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 11:21:43 +0800 Subject: [PATCH 052/489] Better explanation for `fixComboInfo` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 796abf30f4..0c6097bc7e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -249,13 +249,20 @@ namespace osu.Game.Rulesets.Osu.Mods private void fixComboInfo(List hitObjects) { - // First follow the combo indices in the original beatmap - hitObjects.ForEach(x => + // Copy combo indices from the closest preceding object + hitObjects.ForEach(obj => { - var origObj = origHitObjects.FindLast(y => Precision.AlmostBigger(x.StartTime, y.StartTime)); - x.ComboIndex = origObj?.ComboIndex ?? 0; + var closestOrigObj = origHitObjects.FindLast(y => Precision.AlmostBigger(obj.StartTime, y.StartTime)); + + // It shouldn't be possible for origObj to be null + // But if it is, obj should be in the first combo + obj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; }); - // Then reprocess them to ensure continuity in the combo indices and add indices in current combo + + // The copied combo indices may not be continuous if the original map starts and ends a combo in between beats + // e.g. A stream with each object starting a new combo + // So combo indices need to be reprocessed to ensure continuity + // Other kinds of combo info are also added in the process var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); for (var i = 0; i < combos.Count; i++) From f74275a3b5a676fe2f56f1d974ee41b12445d982 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 11:29:10 +0800 Subject: [PATCH 053/489] Moved RNG initialisation to a better place --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 0c6097bc7e..dfce423399 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -93,6 +93,8 @@ namespace osu.Game.Rulesets.Osu.Mods private List origHitObjects; + private Random rng; + #endregion #region Sudden Death (IApplicableFailOverride) @@ -161,6 +163,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); + rng = new Random(Seed.Value.GetValueOrDefault()); var osuBeatmap = (OsuBeatmap)beatmap; @@ -284,8 +287,6 @@ namespace osu.Game.Rulesets.Osu.Mods { if (hitObjects.Count == 0) return; - var rng = new Random(Seed.Value.GetValueOrDefault()); - float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); var direction = MathHelper.TwoPi * nextSingle(); From 71b5ed16c0e5182f1555e3e6cfa739b4d74742d4 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 11:37:00 +0800 Subject: [PATCH 054/489] Avoid using osuTK constants; Use `MathF` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index dfce423399..8bbc6f4703 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -289,7 +289,9 @@ namespace osu.Game.Rulesets.Osu.Mods float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); - var direction = MathHelper.TwoPi * nextSingle(); + const float two_pi = MathF.PI * 2; + + var direction = two_pi * nextSingle(); var maxComboIndex = hitObjects.Last().ComboIndex; for (var i = 0; i < hitObjects.Count; i++) @@ -312,14 +314,14 @@ namespace osu.Game.Rulesets.Osu.Mods do { - if (tryCount > 0) direction = MathHelper.TwoPi * nextSingle(); + if (tryCount > 0) direction = two_pi * nextSingle(); var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) + distance * MathF.Cos(direction), + distance * MathF.Sin(direction) ); relativePos = getRotatedVector(lastPos, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + direction = MathF.Atan2(relativePos.Y, relativePos.X); var newPosition = Vector2.Add(lastPos, relativePos); @@ -332,9 +334,9 @@ namespace osu.Game.Rulesets.Osu.Mods } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); if (obj.LastInCombo) - direction = MathHelper.TwoPi * nextSingle(); + direction = two_pi * nextSingle(); else - direction += distance / distance_cap * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / distance_cap * (nextSingle() * two_pi - MathF.PI); } } From f8fe4ab4828f72cc71bddebe58fe6890d683d906 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 12:02:48 +0800 Subject: [PATCH 055/489] Refactor and rename `isOverlappingWithRecent` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 8bbc6f4703..8a9f9bf224 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -331,7 +331,7 @@ namespace osu.Game.Rulesets.Osu.Mods tryCount++; if (tryCount % 10 == 0) distance *= 0.9f; - } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); + } while (distance >= obj.Radius * 2 && checkForOverlap(hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count), obj)); if (obj.LastInCombo) direction = two_pi * nextSingle(); @@ -470,11 +470,9 @@ namespace osu.Game.Rulesets.Osu.Mods return -1; } - private bool isOverlappingWithRecent(IReadOnlyList hitObjects, int idx) + private bool checkForOverlap(IEnumerable objectsToCheck, OsuHitObject target) { - var target = hitObjects[idx]; - return hitObjects.SkipLast(hitObjects.Count - idx).TakeLast(overlap_check_count) - .Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); + return objectsToCheck.Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); } /// From 877c775e35069ee02bba3a6120036ad602c576da Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 12:20:23 +0800 Subject: [PATCH 056/489] Added comments --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 8a9f9bf224..1ec08b2c40 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -173,11 +173,11 @@ namespace osu.Game.Rulesets.Osu.Mods origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); var hitObjects = generateBeats(osuBeatmap) - .Select(x => + .Select(beat => { var newCircle = new HitCircle(); newCircle.ApplyDefaults(controlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = x; + newCircle.StartTime = beat; return (OsuHitObject)newCircle; }).ToList(); @@ -199,9 +199,13 @@ namespace osu.Game.Rulesets.Osu.Mods var endTime = endObj.GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints + // Ignore timing points after endTime .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) + // Generate the beats .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) + // Remove beats before startTime .Where(beat => Precision.AlmostBigger(beat, startTime)) + // Remove beats during breaks .Where(beat => !isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); @@ -320,6 +324,7 @@ namespace osu.Game.Rulesets.Osu.Mods distance * MathF.Cos(direction), distance * MathF.Sin(direction) ); + // Rotate the new circle away from playfield border relativePos = getRotatedVector(lastPos, relativePos); direction = MathF.Atan2(relativePos.Y, relativePos.X); @@ -428,6 +433,9 @@ namespace osu.Game.Rulesets.Osu.Mods /// Hit samples private IList getSamplesAtTime(IEnumerable hitObjects, double time) { + // Get a hit object that + // either has StartTime equal to the target time + // or has a repeat node at the target time var sampleObj = hitObjects.FirstOrDefault(hitObject => { if (Precision.AlmostEquals(time, hitObject.StartTime)) @@ -550,6 +558,10 @@ namespace osu.Game.Rulesets.Osu.Mods ); } + /// + /// Move the hit object into playfield, taking its radius into account. + /// + /// The hit object to be clamped. private void clampToPlayfield(OsuHitObject obj) { var position = obj.Position; From 6629f8706a741b67224423f25b473bc067e11047 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 12:31:25 +0800 Subject: [PATCH 057/489] Directly fade to gray instead of computing the color values --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 1ec08b2c40..9734b17ce0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -29,7 +29,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -146,8 +145,7 @@ namespace osu.Game.Rulesets.Osu.Mods var colour = drawable.Colour; - var avgColour = colour.AverageColour.Linear; - drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) + drawable.FadeColour(OsuColour.Gray(0.45f)) .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) .FadeColour(colour, undim_duration); From be55c7e075ad103bedd11dedf942ed193c731ea4 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 13:39:50 +0800 Subject: [PATCH 058/489] Minor fixes in comments --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 9734b17ce0..00caea0bfe 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Mods .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) .FadeColour(colour, undim_duration); - // remove approach circles + // Remove approach circles circle.ApproachCircle.Hide(); } } @@ -239,7 +239,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (lastSampleIdx > 0) { - // get samples from the previous hit object if it is closer in time + // Get samples from the previous hit object if it is closer in time if (obj.StartTime - origHitObjects[lastSampleIdx - 1].StartTime < origHitObjects[lastSampleIdx].StartTime - obj.StartTime) lastSampleIdx--; } @@ -259,7 +259,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var closestOrigObj = origHitObjects.FindLast(y => Precision.AlmostBigger(obj.StartTime, y.StartTime)); - // It shouldn't be possible for origObj to be null + // It shouldn't be possible for closestOrigObj to be null // But if it is, obj should be in the first combo obj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; }); From 1a47bc254d4e98407ab7799823fdba3b1992a444 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 14:55:49 +0800 Subject: [PATCH 059/489] Increase acceptable difference for Precision calls --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 39 +++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 00caea0bfe..bd3287634c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -84,6 +84,11 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const double undim_duration = 96; + /// + /// Acceptable difference for timing comparisons + /// + private const double timing_precision = 1; + #endregion #region Private Fields @@ -198,11 +203,11 @@ namespace osu.Game.Rulesets.Osu.Mods var beats = beatmap.ControlPointInfo.TimingPoints // Ignore timing points after endTime - .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) + .Where(timingPoint => almostBigger(endTime, timingPoint.Time)) // Generate the beats .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) // Remove beats before startTime - .Where(beat => Precision.AlmostBigger(beat, startTime)) + .Where(beat => almostBigger(beat, startTime)) // Remove beats during breaks .Where(beat => !isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); @@ -212,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var beat = beats[i]; - if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + if (almostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) beats.RemoveAt(i); } @@ -257,7 +262,7 @@ namespace osu.Game.Rulesets.Osu.Mods // Copy combo indices from the closest preceding object hitObjects.ForEach(obj => { - var closestOrigObj = origHitObjects.FindLast(y => Precision.AlmostBigger(obj.StartTime, y.StartTime)); + var closestOrigObj = origHitObjects.FindLast(y => almostBigger(obj.StartTime, y.StartTime)); // It shouldn't be possible for closestOrigObj to be null // But if it is, obj should be in the first combo @@ -397,10 +402,10 @@ namespace osu.Game.Rulesets.Osu.Mods { return breaks.Any(breakPeriod => { - var firstObjAfterBreak = origHitObjects.First(obj => Precision.AlmostBigger(obj.StartTime, breakPeriod.EndTime)); + var firstObjAfterBreak = origHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); - return Precision.AlmostBigger(time, breakPeriod.StartTime) - && Precision.AlmostBigger(firstObjAfterBreak.StartTime, time); + return almostBigger(time, breakPeriod.StartTime) + && almostBigger(firstObjAfterBreak.StartTime, time); }); } @@ -410,7 +415,7 @@ namespace osu.Game.Rulesets.Osu.Mods int i = 0; var currentTime = timingPoint.Time; - while (Precision.AlmostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) + while (almostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) { beats.Add(Math.Floor(currentTime)); i++; @@ -436,13 +441,13 @@ namespace osu.Game.Rulesets.Osu.Mods // or has a repeat node at the target time var sampleObj = hitObjects.FirstOrDefault(hitObject => { - if (Precision.AlmostEquals(time, hitObject.StartTime)) + if (almostEquals(time, hitObject.StartTime)) return true; if (!(hitObject is IHasPathWithRepeats s)) return false; - if (!Precision.AlmostBigger(time, hitObject.StartTime) - || !Precision.AlmostBigger(s.EndTime, time)) + if (!almostBigger(time, hitObject.StartTime) + || !almostBigger(s.EndTime, time)) return false; return nodeIndexFromTime(s, time - hitObject.StartTime) != -1; @@ -470,7 +475,7 @@ namespace osu.Game.Rulesets.Osu.Mods double spanDuration = curve.Duration / curve.SpanCount(); double nodeIndex = timeSinceStart / spanDuration; - if (Precision.AlmostEquals(nodeIndex - Math.Round(nodeIndex), 0)) + if (almostEquals(nodeIndex - Math.Round(nodeIndex), 0)) return (int)Math.Round(nodeIndex); return -1; @@ -583,6 +588,16 @@ namespace osu.Game.Rulesets.Osu.Mods return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } + private static bool almostBigger(double value1, double value2) + { + return Precision.AlmostBigger(value1, value2, timing_precision); + } + + private static bool almostEquals(double value1, double value2) + { + return Precision.AlmostEquals(value1, value2, timing_precision); + } + #endregion } } From 3eaa04115f3dc6240e0b7231103db9c875168294 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 26 Jun 2021 11:34:10 +0800 Subject: [PATCH 060/489] Use `OsuHitObjectGenerationUtils` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 78 +--------------------- 1 file changed, 2 insertions(+), 76 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index bd3287634c..1a6de87745 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Osu.Utils; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; @@ -328,7 +329,7 @@ namespace osu.Game.Rulesets.Osu.Mods distance * MathF.Sin(direction) ); // Rotate the new circle away from playfield border - relativePos = getRotatedVector(lastPos, relativePos); + relativePos = OsuHitObjectGenerationUtils.RotateAwayFromEdge(lastPos, relativePos, edge_rotation_multiplier); direction = MathF.Atan2(relativePos.Y, relativePos.X); var newPosition = Vector2.Add(lastPos, relativePos); @@ -486,81 +487,6 @@ namespace osu.Game.Rulesets.Osu.Mods return objectsToCheck.Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); } - /// - /// Determines the position of the current hit object relative to the previous one. - /// - /// The position of the current hit object relative to the previous one - private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) - { - var relativeRotationDistance = 0f; - var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); - - if (prevPosChanged.X < playfieldMiddle.X) - { - relativeRotationDistance = Math.Max( - (border_distance_x - prevPosChanged.X) / border_distance_x, - relativeRotationDistance - ); - } - else - { - relativeRotationDistance = Math.Max( - (prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x, - relativeRotationDistance - ); - } - - if (prevPosChanged.Y < playfieldMiddle.Y) - { - relativeRotationDistance = Math.Max( - (border_distance_y - prevPosChanged.Y) / border_distance_y, - relativeRotationDistance - ); - } - else - { - relativeRotationDistance = Math.Max( - (prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y, - relativeRotationDistance - ); - } - - return rotateVectorTowardsVector( - posRelativeToPrev, - Vector2.Subtract(playfieldMiddle, prevPosChanged), - Math.Min(1, relativeRotationDistance * edge_rotation_multiplier) - ); - } - - /// - /// Rotates vector "initial" towards vector "destination" - /// - /// Vector to rotate to "destination" - /// Vector "initial" should be rotated to - /// - /// The angle the vector should be rotated relative to the difference between the angles of - /// the the two vectors. - /// - /// Resulting vector - private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) - { - var initialAngleRad = Math.Atan2(initial.Y, initial.X); - var destAngleRad = Math.Atan2(destination.Y, destination.X); - - var diff = destAngleRad - initialAngleRad; - - while (diff < -Math.PI) diff += 2 * Math.PI; - - while (diff > Math.PI) diff -= 2 * Math.PI; - - var finalAngleRad = initialAngleRad + relativeDistance * diff; - - return new Vector2( - initial.Length * (float)Math.Cos(finalAngleRad), - initial.Length * (float)Math.Sin(finalAngleRad) - ); - } - /// /// Move the hit object into playfield, taking its radius into account. /// From ea8993d6d6363654490d210fcac4ba369ac7d3d0 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 28 Jun 2021 11:33:19 +0800 Subject: [PATCH 061/489] Use `IHasRepeats` instead of `IHasPathWithRepeats` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 1a6de87745..b2513e4983 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -445,7 +445,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (almostEquals(time, hitObject.StartTime)) return true; - if (!(hitObject is IHasPathWithRepeats s)) + if (!(hitObject is IHasRepeats s)) return false; if (!almostBigger(time, hitObject.StartTime) || !almostBigger(s.EndTime, time)) @@ -457,7 +457,7 @@ namespace osu.Game.Rulesets.Osu.Mods IList samples; - if (sampleObj is IHasPathWithRepeats slider) + if (sampleObj is IHasRepeats slider) samples = slider.NodeSamples[nodeIndexFromTime(slider, time - sampleObj.StartTime)]; else samples = sampleObj.Samples; @@ -471,7 +471,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// The slider. /// The time since the start time of the slider. /// Index of the node. -1 if there isn't a node at the specific time. - private int nodeIndexFromTime(IHasPathWithRepeats curve, double timeSinceStart) + private int nodeIndexFromTime(IHasRepeats curve, double timeSinceStart) { double spanDuration = curve.Duration / curve.SpanCount(); double nodeIndex = timeSinceStart / spanDuration; From d4ff4b26f55f7532fc2a67751a5f46e976f8cd15 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 29 Jun 2021 12:49:25 +0800 Subject: [PATCH 062/489] Split part of `addHitSamples` to a subroutine --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 39 +++++++++------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b2513e4983..3ad432a40b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -227,34 +227,13 @@ namespace osu.Game.Rulesets.Osu.Mods private void addHitSamples(IEnumerable hitObjects) { - var lastSampleIdx = 0; - foreach (var obj in hitObjects) { var samples = getSamplesAtTime(origHitObjects, obj.StartTime); - if (samples == null) - { - // If samples aren't available at the exact start time of the object, - // use samples (without additions) in the closest original hit object instead - - while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= obj.StartTime) - lastSampleIdx++; - - if (lastSampleIdx >= origHitObjects.Count) continue; - - if (lastSampleIdx > 0) - { - // Get samples from the previous hit object if it is closer in time - if (obj.StartTime - origHitObjects[lastSampleIdx - 1].StartTime < origHitObjects[lastSampleIdx].StartTime - obj.StartTime) - lastSampleIdx--; - } - - // Remove additions - obj.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); - } - else - obj.Samples = samples; + // If samples aren't available at the exact start time of the object, + // use samples (without additions) in the closest original hit object instead + obj.Samples = samples ?? getClosestHitObject(origHitObjects, obj.StartTime).Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } } @@ -426,6 +405,18 @@ namespace osu.Game.Rulesets.Osu.Mods return beats; } + private OsuHitObject getClosestHitObject(List hitObjects, double time) + { + var closestIdx = hitObjects.FindLastIndex(h => h.StartTime < time); + + if (closestIdx == hitObjects.Count - 1) return hitObjects[closestIdx]; + + // return the closest preceding/succeeding hit object, whoever is closer in time + return hitObjects[closestIdx + 1].StartTime - time < time - hitObjects[closestIdx].StartTime + ? hitObjects[closestIdx + 1] + : hitObjects[closestIdx]; + } + /// /// Get samples (if any) for a specific point in time. /// From 913f7602e4829306aa6aa5e0faa5a97bd6522568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Jun 2021 20:41:08 +0200 Subject: [PATCH 063/489] Change seed control type in line with changes --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3ad432a40b..12daa44477 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -17,6 +17,7 @@ using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -46,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) }; - [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable { Default = null, From 58f80abe322559102ea1c47c5637057cf8aa8ab8 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 11:57:57 +0800 Subject: [PATCH 064/489] Several requested changes - Rename `origHitObjects` to `originalHitObjects` - Use `Value` instead of `GetValueOrDefault()` - Remove `endObj` - Added comments - Rename `closestIdx` to `precedingIndex` - Changed an `almostEquals` call --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 47 +++++++++++++--------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 12daa44477..60ca25a721 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Mods private ControlPointInfo controlPointInfo; - private List origHitObjects; + private List originalHitObjects; private Random rng; @@ -168,14 +168,14 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); - rng = new Random(Seed.Value.GetValueOrDefault()); + rng = new Random(Seed.Value.Value); var osuBeatmap = (OsuBeatmap)beatmap; if (osuBeatmap.HitObjects.Count == 0) return; controlPointInfo = osuBeatmap.ControlPointInfo; - origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); + originalHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); var hitObjects = generateBeats(osuBeatmap) .Select(beat => @@ -199,9 +199,8 @@ namespace osu.Game.Rulesets.Osu.Mods private IEnumerable generateBeats(IBeatmap beatmap) { - var startTime = origHitObjects.First().StartTime; - var endObj = origHitObjects.Last(); - var endTime = endObj.GetEndTime(); + var startTime = originalHitObjects.First().StartTime; + var endTime = originalHitObjects.Last().GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints // Ignore timing points after endTime @@ -230,24 +229,25 @@ namespace osu.Game.Rulesets.Osu.Mods { foreach (var obj in hitObjects) { - var samples = getSamplesAtTime(origHitObjects, obj.StartTime); + var samples = getSamplesAtTime(originalHitObjects, obj.StartTime); // If samples aren't available at the exact start time of the object, // use samples (without additions) in the closest original hit object instead - obj.Samples = samples ?? getClosestHitObject(origHitObjects, obj.StartTime).Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); + obj.Samples = samples ?? getClosestHitObject(originalHitObjects, obj.StartTime).Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } } private void fixComboInfo(List hitObjects) { - // Copy combo indices from the closest preceding object - hitObjects.ForEach(obj => + // Copy combo indices from an original object at the same time or from the closest preceding object + // (Objects lying between two combos are assumed to belong to the preceding combo) + hitObjects.ForEach(newObj => { - var closestOrigObj = origHitObjects.FindLast(y => almostBigger(obj.StartTime, y.StartTime)); + var closestOrigObj = originalHitObjects.FindLast(y => almostBigger(newObj.StartTime, y.StartTime)); // It shouldn't be possible for closestOrigObj to be null // But if it is, obj should be in the first combo - obj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; + newObj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; }); // The copied combo indices may not be continuous if the original map starts and ends a combo in between beats @@ -383,7 +383,7 @@ namespace osu.Game.Rulesets.Osu.Mods { return breaks.Any(breakPeriod => { - var firstObjAfterBreak = origHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); + var firstObjAfterBreak = originalHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); return almostBigger(time, breakPeriod.StartTime) && almostBigger(firstObjAfterBreak.StartTime, time); @@ -408,14 +408,14 @@ namespace osu.Game.Rulesets.Osu.Mods private OsuHitObject getClosestHitObject(List hitObjects, double time) { - var closestIdx = hitObjects.FindLastIndex(h => h.StartTime < time); + var precedingIndex = hitObjects.FindLastIndex(h => h.StartTime < time); - if (closestIdx == hitObjects.Count - 1) return hitObjects[closestIdx]; + if (precedingIndex == hitObjects.Count - 1) return hitObjects[precedingIndex]; // return the closest preceding/succeeding hit object, whoever is closer in time - return hitObjects[closestIdx + 1].StartTime - time < time - hitObjects[closestIdx].StartTime - ? hitObjects[closestIdx + 1] - : hitObjects[closestIdx]; + return hitObjects[precedingIndex + 1].StartTime - time < time - hitObjects[precedingIndex].StartTime + ? hitObjects[precedingIndex + 1] + : hitObjects[precedingIndex]; } /// @@ -468,7 +468,7 @@ namespace osu.Game.Rulesets.Osu.Mods double spanDuration = curve.Duration / curve.SpanCount(); double nodeIndex = timeSinceStart / spanDuration; - if (almostEquals(nodeIndex - Math.Round(nodeIndex), 0)) + if (almostEquals(nodeIndex, Math.Round(nodeIndex))) return (int)Math.Round(nodeIndex); return -1; @@ -501,6 +501,15 @@ namespace osu.Game.Rulesets.Osu.Mods obj.Position = position; } + /// + /// Re-maps a number from one range to another. + /// + /// The number to be re-mapped. + /// Beginning of the original range. + /// End of the original range. + /// Beginning of the new range. + /// End of the new range. + /// The re-mapped number. private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) { return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; From 34be437d7a6b9af4d71581e4b67481883e78fdcf Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 12:21:41 +0800 Subject: [PATCH 065/489] Added `definitelyBigger` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 60ca25a721..a39dd7a90a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Mods var beats = beatmap.ControlPointInfo.TimingPoints // Ignore timing points after endTime - .Where(timingPoint => almostBigger(endTime, timingPoint.Time)) + .Where(timingPoint => !definitelyBigger(timingPoint.Time, endTime)) // Generate the beats .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) // Remove beats before startTime @@ -218,7 +218,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var beat = beats[i]; - if (almostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + if (!definitelyBigger(beats[i + 1] - beat, beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2)) beats.RemoveAt(i); } @@ -386,7 +386,7 @@ namespace osu.Game.Rulesets.Osu.Mods var firstObjAfterBreak = originalHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); return almostBigger(time, breakPeriod.StartTime) - && almostBigger(firstObjAfterBreak.StartTime, time); + && definitelyBigger(firstObjAfterBreak.StartTime, time); }); } @@ -396,7 +396,7 @@ namespace osu.Game.Rulesets.Osu.Mods int i = 0; var currentTime = timingPoint.Time; - while (almostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) + while (!definitelyBigger(currentTime, mapEndTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) { beats.Add(Math.Floor(currentTime)); i++; @@ -439,6 +439,8 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(hitObject is IHasRepeats s)) return false; + // If time is outside the duration of the IHasRepeats, + // then this hitObject isn't the one we want if (!almostBigger(time, hitObject.StartTime) || !almostBigger(s.EndTime, time)) return false; @@ -520,6 +522,11 @@ namespace osu.Game.Rulesets.Osu.Mods return Precision.AlmostBigger(value1, value2, timing_precision); } + private static bool definitelyBigger(double value1, double value2) + { + return Precision.DefinitelyBigger(value1, value2, timing_precision); + } + private static bool almostEquals(double value1, double value2) { return Precision.AlmostEquals(value1, value2, timing_precision); From b0a619bb4cb0837265af38dcb86a597617fcb777 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 12:49:34 +0800 Subject: [PATCH 066/489] Prevent multiple enumeration in `checkForOverlap` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index a39dd7a90a..cc3811c9b8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -300,6 +300,9 @@ namespace osu.Game.Rulesets.Osu.Mods var tryCount = 0; + // for checking overlap + var precedingObjects = hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count).ToList(); + do { if (tryCount > 0) direction = two_pi * nextSingle(); @@ -320,7 +323,7 @@ namespace osu.Game.Rulesets.Osu.Mods tryCount++; if (tryCount % 10 == 0) distance *= 0.9f; - } while (distance >= obj.Radius * 2 && checkForOverlap(hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count), obj)); + } while (distance >= obj.Radius * 2 && checkForOverlap(precedingObjects, obj)); if (obj.LastInCombo) direction = two_pi * nextSingle(); From 1470bb15634c0946a08c91fd484b643a5e542659 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 3 Jul 2021 13:02:39 +0800 Subject: [PATCH 067/489] Use `IHidesApproachCircles` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index cc3811c9b8..5ca9a15a0f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, - IHasSeed, IMutateApproachCircles + IHasSeed, IHidesApproachCircles { public override string Name => "Target"; public override string Acronym => "TP"; @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles) }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From d1862d8cff46945bfb7d90bfc984e1fb176f66af Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 4 Jul 2021 10:01:56 +0800 Subject: [PATCH 068/489] Rename `map` to `mapRange` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5ca9a15a0f..27c352070a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -291,7 +291,7 @@ namespace osu.Game.Rulesets.Osu.Mods var distance = maxComboIndex == 0 ? (float)obj.Radius - : map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); + : mapRange(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); if (obj.NewCombo) distance *= 1.5f; if (obj.Kiai) distance *= 1.2f; distance = Math.Min(distance_cap, distance); @@ -515,7 +515,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Beginning of the new range. /// End of the new range. /// The re-mapped number. - private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) + private static float mapRange(float value, float fromLow, float fromHigh, float toLow, float toHigh) { return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } From d3bb4ddbee09ebcab963add6fcce38945d01b99f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 5 Jul 2021 19:05:49 +0900 Subject: [PATCH 069/489] Add an ad-hoc way to provide dependency to children --- .../Visual/DependencyProvidingContainer.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 osu.Game/Tests/Visual/DependencyProvidingContainer.cs diff --git a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs new file mode 100644 index 0000000000..cd3ae1123b --- /dev/null +++ b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs @@ -0,0 +1,50 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Tests.Visual +{ + /// + /// A which providing ad-hoc dependencies to the child drawables. + /// + /// To provide a dependency, specify the dependency type to , then specify the dependency value to either or . + /// For each type specified in , the first value compatible with the type is selected and provided to the children. + /// + /// + /// + /// The and values of the dependencies must be set while this is not loaded. + /// + public class DependencyProvidingContainer : Container + { + /// + /// The types of the dependencies provided to the children. + /// + // TODO: should be an init-only property when C# 9 + public Type[] Types { get; set; } = Array.Empty(); + + /// + /// The dependency values provided to the children. + /// + public object[] Values { get; set; } = Array.Empty(); + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencyContainer = new DependencyContainer(base.CreateChildDependencies(parent)); + + foreach (var type in Types) + { + object value = Values.FirstOrDefault(v => type.IsInstanceOfType(v)) ?? + Children.FirstOrDefault(d => type.IsInstanceOfType(d)) ?? + throw new InvalidOperationException($"The type {type} is specified in this {nameof(DependencyProvidingContainer)}, but no corresponding value is provided."); + + dependencyContainer.CacheAs(type, value); + } + + return dependencyContainer; + } + } +} From 5a0a223b1b96d2f10630d888d54f68e06a9acb95 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 5 Jul 2021 19:07:29 +0900 Subject: [PATCH 070/489] Use `DependencyProvidingContainer` in `TestSceneCatcher` --- .../TestSceneCatcher.cs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 8359657f84..0cf5e2b7a0 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,22 +31,9 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - - private readonly Container trailContainer; - private TestCatcher catcher; - public TestSceneCatcher() - { - Add(trailContainer = new Container - { - Anchor = Anchor.Centre, - Depth = -1 - }); - Add(droppedObjectContainer = new DroppedObjectContainer()); - } + private DroppedObjectContainer droppedObjectContainer; [SetUp] public void SetUp() => Schedule(() => @@ -56,13 +43,24 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - if (catcher != null) - Remove(catcher); - - Add(catcher = new TestCatcher(trailContainer, difficulty) + var trailContainer = new Container { + Anchor = Anchor.Centre, + }; + Child = new DependencyProvidingContainer + { + Types = new[] + { + typeof(DroppedObjectContainer), + }, + Children = new Drawable[] + { + droppedObjectContainer = new DroppedObjectContainer(), + catcher = new TestCatcher(trailContainer, difficulty), + trailContainer + }, Anchor = Anchor.Centre - }); + }; }); [Test] From c67f756c754426af42eecc6d5ccb19a23f4cdc5d Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 8 Jul 2021 11:52:43 +0300 Subject: [PATCH 071/489] Change approach rate bonuses to be less punishing on mid-length maps --- .../Difficulty/OsuPerformanceCalculator.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 749d7d1b41..40ae1fca91 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -98,11 +98,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) - approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33); + approachRateFactor += Attributes.ApproachRate - 10.33; else if (Attributes.ApproachRate < 8.0) - approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate); + approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); - aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); + aimValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(h => h is OsuModHidden)) @@ -145,10 +145,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) - approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33); + approachRateFactor = Attributes.ApproachRate - 10.33; speedValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); + double approachRateFactor = 0.0; + if (Attributes.ApproachRate > 10.33) + approachRateFactor += Attributes.ApproachRate - 10.33; + + speedValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; + if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); From 592d3fdf006f63fd020cb04197f24523a104c304 Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 8 Jul 2021 11:54:58 +0300 Subject: [PATCH 072/489] Cleanup --- .../Difficulty/OsuPerformanceCalculator.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 40ae1fca91..d53af98f92 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) - approachRateFactor += Attributes.ApproachRate - 10.33; + approachRateFactor = Attributes.ApproachRate - 10.33; else if (Attributes.ApproachRate < 8.0) - approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); + approachRateFactor = 0.025 * (8.0 - Attributes.ApproachRate); aimValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; @@ -147,12 +147,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (Attributes.ApproachRate > 10.33) approachRateFactor = Attributes.ApproachRate - 10.33; - speedValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); - - double approachRateFactor = 0.0; - if (Attributes.ApproachRate > 10.33) - approachRateFactor += Attributes.ApproachRate - 10.33; - speedValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; if (mods.Any(m => m is OsuModHidden)) From 02298c2cf40e2b7df25d44c056d9507cb338d1fc Mon Sep 17 00:00:00 2001 From: StanR Date: Fri, 9 Jul 2021 00:06:05 +0300 Subject: [PATCH 073/489] Fix incorrect curve, move total hits factor into a separate variable for clarity --- .../Difficulty/OsuPerformanceCalculator.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d53af98f92..8a3c426381 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -102,7 +102,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty else if (Attributes.ApproachRate < 8.0) approachRateFactor = 0.025 * (8.0 - Attributes.ApproachRate); - aimValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; + double approachRateTotalHitsFactor = 1.0 / (1.0 + Math.Exp(-(0.007 * (totalHits - 400)))); + + aimValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(h => h is OsuModHidden)) @@ -147,7 +149,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (Attributes.ApproachRate > 10.33) approachRateFactor = Attributes.ApproachRate - 10.33; - speedValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; + double approachRateTotalHitsFactor = 1.0 / (1.0 + Math.Exp(-(0.007 * (totalHits - 400)))); + + speedValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); From 0db316d644064bd168454fcb1354b804ebe23918 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 16:08:20 +0900 Subject: [PATCH 074/489] Add password scaffolding --- osu.Game/Online/Rooms/Room.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b28680ffef..9270c4b69d 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -35,6 +35,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("channel_id")] public readonly Bindable ChannelId = new Bindable(); + [JsonProperty("password")] + public readonly Bindable Password = new Bindable(); + [Cached] [JsonIgnore] public readonly Bindable Category = new Bindable(); @@ -143,6 +146,7 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; + Password.Value = other.Password.Value; Availability.Value = other.Availability.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; From 24f330e5c1ae697f8e02eefb314fcefc559f9d4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 16:12:28 +0900 Subject: [PATCH 075/489] Avoid `MatchSettingsOverlay` base class potentially accessing an uninitialised field --- .../OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 4 ++++ .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 7 ++----- .../OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs | 7 ++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 5699da740c..61bb39d0c5 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -25,8 +25,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components private void load() { Masking = true; + + Add(Settings = CreateSettings()); } + protected abstract OnlinePlayComposite CreateSettings(); + protected override void PopIn() { Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 2e180f31fd..81ee580a24 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -27,16 +27,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerMatchSettingsOverlay : MatchSettingsOverlay { - [BackgroundDependencyLoader] - private void load() - { - Child = Settings = new MatchSettings + protected override OnlinePlayComposite CreateSettings() + => new MatchSettings { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, SettingsApplied = Hide }; - } protected class MatchSettings : OnlinePlayComposite { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 5eb2b545cb..88ac5ef6e5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -26,16 +26,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public Action EditPlaylist; - [BackgroundDependencyLoader] - private void load() - { - Child = Settings = new MatchSettings + protected override OnlinePlayComposite CreateSettings() + => new MatchSettings { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, EditPlaylist = () => EditPlaylist?.Invoke() }; - } protected class MatchSettings : OnlinePlayComposite { From 4fd6f2101c2d890372fc0c096d09df5c39284b6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 16:28:48 +0900 Subject: [PATCH 076/489] Add password textbox input --- osu.Game/Online/Rooms/Room.cs | 1 - .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 9270c4b69d..bccb6f854c 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -146,7 +146,6 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; - Password.Value = other.Password.Value; Availability.Value = other.Availability.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 81ee580a24..bc2f263a68 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -44,6 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public OsuTextBox NameField, MaxParticipantsField; public RoomAvailabilityPicker AvailabilityPicker; public GameTypePicker TypePicker; + public OsuTextBox PasswordTextBox; public TriangleButton ApplyButton; public OsuSpriteText ErrorText; @@ -190,12 +191,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }, new Section("Password (optional)") { - Alpha = disabled_alpha, - Child = new SettingsPasswordTextBox + Child = PasswordTextBox = new SettingsPasswordTextBox { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, - ReadOnly = true, }, }, } @@ -317,6 +316,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match currentRoom.Value.Name.Value = NameField.Text; currentRoom.Value.Availability.Value = AvailabilityPicker.Current.Value; currentRoom.Value.Type.Value = TypePicker.Current.Value; + currentRoom.Value.Password.Value = PasswordTextBox.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) currentRoom.Value.MaxParticipants.Value = max; From 6a74fde0824fe127c614a06ef5b8afcc7c105f71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 18:53:13 +0900 Subject: [PATCH 077/489] Add `has_password` flag and region post only parameters --- osu.Game/Online/Rooms/Room.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index bccb6f854c..d1701d544e 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -35,9 +35,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("channel_id")] public readonly Bindable ChannelId = new Bindable(); - [JsonProperty("password")] - public readonly Bindable Password = new Bindable(); - [Cached] [JsonIgnore] public readonly Bindable Category = new Bindable(); @@ -51,10 +48,6 @@ namespace osu.Game.Online.Rooms set => Category.Value = value; } - [Cached] - [JsonIgnore] - public readonly Bindable Duration = new Bindable(); - [Cached] [JsonIgnore] public readonly Bindable MaxAttempts = new Bindable(); @@ -79,6 +72,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); + [JsonProperty("has_password")] + public readonly BindableBool HasPassword = new BindableBool(); + [Cached] [JsonProperty("recent_participants")] public readonly BindableList RecentParticipants = new BindableList(); @@ -87,6 +83,15 @@ namespace osu.Game.Online.Rooms [JsonProperty("participant_count")] public readonly Bindable ParticipantCount = new Bindable(); + #region Properties only used for room creation request + + [JsonProperty("password")] + public readonly Bindable Password = new Bindable(); + + [Cached] + [JsonIgnore] + public readonly Bindable Duration = new Bindable(); + [JsonProperty("duration")] private int? duration { @@ -100,6 +105,8 @@ namespace osu.Game.Online.Rooms } } + #endregion + // Only supports retrieval for now [Cached] [JsonProperty("ends_at")] From 2ca11d458a3ea885ebdae8a033fd0c9eda984317 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 00:53:10 +0900 Subject: [PATCH 078/489] Add password to room settings and multiplayer lounge interface --- .../Multiplayer/IMultiplayerLoungeServer.cs | 10 +++++++++ .../Multiplayer/InvalidPasswordException.cs | 22 +++++++++++++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 2 ++ .../Multiplayer/MultiplayerRoomSettings.cs | 5 +++++ 4 files changed, 39 insertions(+) create mode 100644 osu.Game/Online/Multiplayer/InvalidPasswordException.cs diff --git a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs index 4640640c5f..a04ec53578 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs @@ -15,6 +15,16 @@ namespace osu.Game.Online.Multiplayer /// /// The databased room ID. /// If the user is already in the requested (or another) room. + /// If the room required a password. Task JoinRoom(long roomId); + + /// + /// Request to join a multiplayer room with a provided password. + /// + /// The databased room ID. + /// The password for the join request. + /// If the user is already in the requested (or another) room. + /// If the room provided password was incorrect. + Task JoinRoom(long roomId, string password); } } diff --git a/osu.Game/Online/Multiplayer/InvalidPasswordException.cs b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs new file mode 100644 index 0000000000..0441aea287 --- /dev/null +++ b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Runtime.Serialization; +using Microsoft.AspNetCore.SignalR; + +namespace osu.Game.Online.Multiplayer +{ + [Serializable] + public class InvalidPasswordException : HubException + { + public InvalidPasswordException() + { + } + + protected InvalidPasswordException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 2e65f7cf1c..a9f4dc9e2f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -212,6 +212,8 @@ namespace osu.Game.Online.Multiplayer return ChangeSettings(new MultiplayerRoomSettings { Name = name.GetOr(Room.Settings.Name), + // TODO: add changing support + Password = Room.Settings.Password, BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index ee72df4c10..4e94c5982f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -36,12 +36,16 @@ namespace osu.Game.Online.Multiplayer [Key(6)] public long PlaylistItemId { get; set; } + [Key(7)] + public string Password { get; set; } = string.Empty; + public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && BeatmapChecksum == other.BeatmapChecksum && RequiredMods.SequenceEqual(other.RequiredMods) && AllowedMods.SequenceEqual(other.AllowedMods) && RulesetID == other.RulesetID + && Password.Equals(other.Password, StringComparison.Ordinal) && Name.Equals(other.Name, StringComparison.Ordinal) && PlaylistItemId == other.PlaylistItemId; @@ -49,6 +53,7 @@ namespace osu.Game.Online.Multiplayer + $" Beatmap:{BeatmapID} ({BeatmapChecksum})" + $" RequiredMods:{string.Join(',', RequiredMods)}" + $" AllowedMods:{string.Join(',', AllowedMods)}" + + $" Password:{(string.IsNullOrEmpty(Password) ? "no" : "yes")}" + $" Ruleset:{RulesetID}" + $" Item:{PlaylistItemId}"; } From 5148069efe501424d788993f7dc719470e551b7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 16:01:45 +0900 Subject: [PATCH 079/489] Update signatures in line with no-overload methods (unsupported by signalr) --- osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs | 2 +- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 3 ++- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 4 ++-- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 5 +++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs index a04ec53578..0a618c8f5c 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs @@ -25,6 +25,6 @@ namespace osu.Game.Online.Multiplayer /// The password for the join request. /// If the user is already in the requested (or another) room. /// If the room provided password was incorrect. - Task JoinRoom(long roomId, string password); + Task JoinRoomWithPassword(long roomId, string password); } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index a9f4dc9e2f..543b3cd752 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -152,8 +152,9 @@ namespace osu.Game.Online.Multiplayer /// Joins the with a given ID. /// /// The room ID. + /// An optional password to use when joining the room. /// The joined . - protected abstract Task JoinRoom(long roomId); + protected abstract Task JoinRoom(long roomId, string? password = null); public Task LeaveRoom() { diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index cf1e18e059..726e26ebe1 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -62,12 +62,12 @@ namespace osu.Game.Online.Multiplayer } } - protected override Task JoinRoom(long roomId) + protected override Task JoinRoom(long roomId, string? password = null) { if (!IsConnected.Value) return Task.FromCanceled(new CancellationToken(true)); - return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoom), roomId); + return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty); } protected override Task LeaveRoomInternal() diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index b0c8d6d19b..adc632a2b1 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability); } - protected override Task JoinRoom(long roomId) + protected override Task JoinRoom(long roomId, string? password = null) { var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == roomId); @@ -134,7 +134,8 @@ namespace osu.Game.Tests.Visual.Multiplayer BeatmapChecksum = apiRoom.Playlist.Last().Beatmap.Value.MD5Hash, RequiredMods = apiRoom.Playlist.Last().RequiredMods.Select(m => new APIMod(m)).ToArray(), AllowedMods = apiRoom.Playlist.Last().AllowedMods.Select(m => new APIMod(m)).ToArray(), - PlaylistItemId = apiRoom.Playlist.Last().ID + PlaylistItemId = apiRoom.Playlist.Last().ID, + Password = password ?? string.Empty, }, Users = { localUser }, Host = localUser From 84b0a3290ce2ff6767308797f46e76770d020204 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 16:01:52 +0900 Subject: [PATCH 080/489] Add multiplayer lounge test coverage --- .../TestSceneMultiplayerLoungeSubScreen.cs | 44 +++++++++++++++++++ .../Visual/OnlinePlay/BasicTestRoomManager.cs | 5 ++- 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs new file mode 100644 index 0000000000..0d90b75be7 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -0,0 +1,44 @@ +// 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.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Tests.Visual.OnlinePlay; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene + { + protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager; + + private LoungeSubScreen loungeScreen; + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("push screen", () => LoadScreen(loungeScreen = new MultiplayerLoungeSubScreen())); + + AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen()); + } + + [Test] + public void TestJoinRoomWithoutPassword() + { + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false)); + } + + [Test] + public void TestJoinRoomWithPassword() + { + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + } + + private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First(); + } +} diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index 813e617ac5..f2ca3d6357 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay { } - public void AddRooms(int count, RulesetInfo ruleset = null) + public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false) { for (int i = 0; i < count; i++) { @@ -53,7 +53,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay Name = { Value = $"Room {i}" }, Host = { Value = new User { Username = "Host" } }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, - Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal } + Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, + Password = { Value = withPassword ? "password" : string.Empty } }; if (ruleset != null) From 08c40938db22e20a6ece17b1e26776a8664629e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 13:50:52 +0900 Subject: [PATCH 081/489] Add support for updating a room's password --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 8 ++++---- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 543b3cd752..052489ca97 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -127,7 +127,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(room.RoomID.Value != null); // Join the server-side room. - var joinedRoom = await JoinRoom(room.RoomID.Value.Value).ConfigureAwait(false); + var joinedRoom = await JoinRoom(room.RoomID.Value.Value, room.Password.Value).ConfigureAwait(false); Debug.Assert(joinedRoom != null); // Populate users. @@ -190,8 +190,9 @@ namespace osu.Game.Online.Multiplayer /// A room must be joined for this to have any effect. /// /// The new room name, if any. + /// The new password, if any. /// The new room playlist item, if any. - public Task ChangeSettings(Optional name = default, Optional item = default) + public Task ChangeSettings(Optional name = default, Optional password = default, Optional item = default) { if (Room == null) throw new InvalidOperationException("Must be joined to a match to change settings."); @@ -213,8 +214,7 @@ namespace osu.Game.Online.Multiplayer return ChangeSettings(new MultiplayerRoomSettings { Name = name.GetOr(Room.Settings.Name), - // TODO: add changing support - Password = Room.Settings.Password, + Password = password.GetOr(Room.Settings.Password), BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index bc2f263a68..fceb124e0a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -303,7 +303,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) { - client.ChangeSettings(name: NameField.Text).ContinueWith(t => Schedule(() => + client.ChangeSettings(name: NameField.Text, password: PasswordTextBox.Text).ContinueWith(t => Schedule(() => { if (t.IsCompletedSuccessfully) onSuccess(currentRoom.Value); From f35d55c32f3805fe87e54b5c094c25be4975d154 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 14:14:22 +0900 Subject: [PATCH 082/489] Fix `HasPassword` not being in sync with `Password` value for client-side rooms --- osu.Game/Online/Rooms/Room.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index d1701d544e..416dc7e5c0 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -126,6 +126,11 @@ namespace osu.Game.Online.Rooms [JsonIgnore] public readonly Bindable Position = new Bindable(-1); + public Room() + { + Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue)); + } + /// /// Create a copy of this room without online information. /// Should be used to create a local copy of a room for submitting in the future. From 3c49b46c5fdfedd20691c307a3797529b3c18ed6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 14:14:58 +0900 Subject: [PATCH 083/489] Add lock overlay for rooms which are password protected --- .../TestSceneLoungeRoomsContainer.cs | 6 +++ .../Lounge/Components/DrawableRoom.cs | 43 ++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 75cc687ee8..798748b4df 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -123,6 +123,12 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3); } + [Test] + public void TestPasswordProtectedRooms() + { + AddStep("add rooms", () => RoomManager.AddRooms(3, withPassword: true)); + } + private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; private void joinRequested(Room room) => room.Status.Value = new JoinedRoomStatus(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 35782c6104..6fec872a11 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -46,6 +47,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private BeatmapManager beatmaps { get; set; } + private Container content; + public readonly Room Room; private SelectionState state; @@ -124,7 +127,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(SELECTION_BORDER_WIDTH), - Child = new Container + Child = content = new Container { RelativeSizeAxes = Axes.Both, Masking = true, @@ -204,6 +207,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, }, }; + + if (Room.HasPassword.Value) + { + content.Add(new PasswordProtectedIcon()); + } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -245,5 +253,38 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components parentScreen?.OpenNewRoom(Room.CreateCopy()); }) }; + + private class PasswordProtectedIcon : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; + + Size = new Vector2(32); + + InternalChildren = new Drawable[] + { + new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopCentre, + Colour = colours.Gray5, + Rotation = 45, + RelativeSizeAxes = Axes.Both, + Width = 2, + }, + new SpriteIcon + { + Icon = FontAwesome.Solid.Lock, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding(6), + Size = new Vector2(14), + } + }; + } + } } } From 9f9d7f9125c61fde650d6dff79c031da148733d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 16:08:12 +0900 Subject: [PATCH 084/489] Add remaining pieces of password flow (for osu-web join request) --- .../TestScenePlaylistsMatchSettingsOverlay.cs | 2 +- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 5 +++-- osu.Game/Online/Rooms/JoinRoomRequest.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 5 ++++- osu.Game/Screens/OnlinePlay/IRoomManager.cs | 7 +++++-- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 4 ++-- .../OnlinePlay/Multiplayer/MultiplayerRoomManager.cs | 10 +++++----- .../Tests/Visual/OnlinePlay/BasicTestRoomManager.cs | 2 +- 8 files changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index a320cb240f..cdc655500d 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -152,7 +152,7 @@ namespace osu.Game.Tests.Visual.Playlists onSuccess?.Invoke(room); } - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => throw new NotImplementedException(); + public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) => throw new NotImplementedException(); public void PartRoom() => throw new NotImplementedException(); } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 052489ca97..42d436ef11 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -115,7 +115,8 @@ namespace osu.Game.Online.Multiplayer /// Joins the for a given API . /// /// The API . - public async Task JoinRoom(Room room) + /// An optional password to use for the join operation. + public async Task JoinRoom(Room room, string? password = null) { var cancellationSource = joinCancellationSource = new CancellationTokenSource(); @@ -127,7 +128,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(room.RoomID.Value != null); // Join the server-side room. - var joinedRoom = await JoinRoom(room.RoomID.Value.Value, room.Password.Value).ConfigureAwait(false); + var joinedRoom = await JoinRoom(room.RoomID.Value.Value, password ?? room.Password.Value).ConfigureAwait(false); Debug.Assert(joinedRoom != null); // Populate users. diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index faa20a3e6c..a82dc5a859 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -23,6 +23,6 @@ namespace osu.Game.Online.Rooms return req; } - protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}"; + protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}?password={room.Password.Value}"; } } diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 227a772b2d..da02f9624f 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -84,8 +84,11 @@ namespace osu.Game.Screens.OnlinePlay.Components private JoinRoomRequest currentJoinRoomRequest; - public virtual void JoinRoom(Room room, Action onSuccess = null, Action onError = null) + public virtual void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { + // todo: send into JoinRoomRequest directly? + room.Password.Value = password; + currentJoinRoomRequest?.Cancel(); currentJoinRoomRequest = new JoinRoomRequest(room); diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs index 8ff02536f3..34c1393ff1 100644 --- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs @@ -6,6 +6,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Rooms; +#nullable enable + namespace osu.Game.Screens.OnlinePlay { [Cached(typeof(IRoomManager))] @@ -32,15 +34,16 @@ namespace osu.Game.Screens.OnlinePlay /// The to create. /// An action to be invoked if the creation succeeds. /// An action to be invoked if an error occurred. - void CreateRoom(Room room, Action onSuccess = null, Action onError = null); + void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null); /// /// Joins a . /// /// The to join. must be populated. + /// An optional password to use for the join operation. /// /// - void JoinRoom(Room room, Action onSuccess = null, Action onError = null); + void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null); /// /// Parts the currently-joined . diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f24577a8a5..a8b80655f9 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -167,14 +167,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge filter.HoldFocus = false; } - private void joinRequested(Room room) + public void Join(Room room, string password) { if (joiningRoomOperation != null) return; joiningRoomOperation = ongoingOperationTracker?.BeginOperation(); - RoomManager?.JoinRoom(room, r => + RoomManager?.JoinRoom(room, password, r => { Open(room); joiningRoomOperation?.Dispose(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 8526196902..cbba4babe5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -38,9 +38,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); + => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError); - public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) + public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { if (!multiplayerClient.IsConnected.Value) { @@ -56,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return; } - base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); + base.JoinRoom(room, password, r => joinMultiplayerRoom(r, password, onSuccess, onError), onError); } public override void PartRoom() @@ -79,11 +79,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }); } - private void joinMultiplayerRoom(Room room, Action onSuccess = null, Action onError = null) + private void joinMultiplayerRoom(Room room, string password, Action onSuccess = null, Action onError = null) { Debug.Assert(room.RoomID.Value != null); - multiplayerClient.JoinRoom(room).ContinueWith(t => + multiplayerClient.JoinRoom(room, password).ContinueWith(t => { if (t.IsCompletedSuccessfully) Schedule(() => onSuccess?.Invoke(room)); diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index f2ca3d6357..5d3fa34ec5 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay onSuccess?.Invoke(room); } - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); + public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); public void PartRoom() { From e25b3518dc0988149a48ac4b8a91cddc644606c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Jul 2021 10:13:57 +0900 Subject: [PATCH 085/489] Make password popover display inside `RoomsContainer` rooms --- .../Lounge/Components/DrawableRoom.cs | 100 +++++++++++++++++- .../Lounge/Components/RoomsContainer.cs | 49 +++------ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- 3 files changed, 114 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 6fec872a11..bc860772b7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -14,12 +14,15 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osuTK; @@ -27,7 +30,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable, IHasContextMenu + public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable, IHasContextMenu, IHasPopover, IKeyBindingHandler { public const float SELECTION_BORDER_WIDTH = 4; private const float corner_radius = 5; @@ -47,6 +50,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private BeatmapManager beatmaps { get; set; } + [Resolved] + private Bindable selectedRoom { get; set; } + + [Resolved(canBeNull: true)] + private LoungeSubScreen lounge { get; set; } + private Container content; public readonly Room Room; @@ -232,6 +241,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Alpha = 0; } + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Select: + // TODO: this needs to be able to show the popover on demand. + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } + protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; private class RoomName : OsuSpriteText @@ -286,5 +311,78 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }; } } + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (selectedRoom.Value != Room) + return true; + + return base.OnMouseDown(e); + } + + protected override bool OnClick(ClickEvent e) + { + if (Room != selectedRoom.Value) + { + selectedRoom.Value = Room; + return true; + } + + if (Room.HasPassword.Value) + // we want our popover to show. this is a bit of a hack. + return false; + + lounge?.Join(Room, null); + + return base.OnClick(e); + } + + public Popover GetPopover() => new PasswordEntryPopover(Room); + + public class PasswordEntryPopover : Popover + { + [Resolved(canBeNull: true)] + private LoungeSubScreen lounge { get; set; } + + public PasswordEntryPopover(Room room) + { + OsuPasswordTextBox passwordTextbox; + + Child = new Container + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = Color4.OliveDrab, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + Margin = new MarginPadding(10), + Spacing = new Vector2(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + passwordTextbox = new OsuPasswordTextBox + { + Width = 200, + }, + new TriangleButton + { + Width = 80, + Text = "Join Room", + Action = () => lounge?.Join(room, passwordTextbox.Text) + } + } + }, + } + }; + } + + protected override Drawable CreateArrow() => Drawable.Empty(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 5f135a3e90..5a9721a8e3 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Threading; @@ -24,8 +25,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RoomsContainer : CompositeDrawable, IKeyBindingHandler { - public Action JoinRequested; - private readonly IBindableList rooms = new BindableList(); private readonly FillFlowContainer roomFlow; @@ -51,16 +50,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new OsuContextMenuContainer + InternalChild = new PopoverContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = roomFlow = new FillFlowContainer + Child = new OsuContextMenuContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2), + Child = roomFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2), + } } }; } @@ -121,19 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { foreach (var room in rooms) { - roomFlow.Add(new DrawableRoom(room) - { - Action = () => - { - if (room == selectedRoom.Value) - { - joinSelected(); - return; - } - - selectRoom(room); - } - }); + roomFlow.Add(new DrawableRoom(room)); } Filter(filter?.Value); @@ -150,7 +142,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.Remove(toRemove); - selectRoom(null); + selectedRoom.Value = null; } } @@ -160,18 +152,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.SetLayoutPosition(room, room.Room.Position.Value); } - private void selectRoom(Room room) => selectedRoom.Value = room; - - private void joinSelected() - { - if (selectedRoom.Value == null) return; - - JoinRequested?.Invoke(selectedRoom.Value); - } - protected override bool OnClick(ClickEvent e) { - selectRoom(null); + selectedRoom.Value = null; return base.OnClick(e); } @@ -181,10 +164,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { switch (action) { - case GlobalAction.Select: - joinSelected(); - return true; - case GlobalAction.SelectNext: beginRepeatSelection(() => selectNext(1), action); return true; @@ -253,7 +232,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components // we already have a valid selection only change selection if we still have a room to switch to. if (room != null) - selectRoom(room); + selectedRoom.Value = room; } #endregion diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index a8b80655f9..11298037d0 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge RelativeSizeAxes = Axes.Both, ScrollbarOverlapsContent = false, Padding = new MarginPadding(10), - Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } + Child = roomsContainer = new RoomsContainer() }, loadingLayer = new LoadingLayer(true), } From a3e0168a46d5f2ff87f08ed465ac94178256b84b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 14:35:08 +0900 Subject: [PATCH 086/489] Update tests --- .../TestSceneLoungeRoomsContainer.cs | 20 +------------------ .../TestScenePlaylistsLoungeSubScreen.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 2 +- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 798748b4df..4d5bf8f225 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -4,13 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Visual.OnlinePlay; -using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer @@ -29,7 +27,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 0.5f, - JoinRequested = joinRequested }; }); @@ -43,11 +40,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("has 2 rooms", () => container.Rooms.Count == 2); AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); - AddStep("select first room", () => container.Rooms.First().Action?.Invoke()); + AddStep("select first room", () => container.Rooms.First().Click()); AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First())); - - AddStep("join first room", () => container.Rooms.First().Action?.Invoke()); - AddAssert("first room joined", () => RoomManager.Rooms.First().Status.Value is JoinedRoomStatus); } [Test] @@ -66,9 +60,6 @@ namespace osu.Game.Tests.Visual.Multiplayer press(Key.Down); press(Key.Down); AddAssert("last room selected", () => checkRoomSelected(RoomManager.Rooms.Last())); - - press(Key.Enter); - AddAssert("last room joined", () => RoomManager.Rooms.Last().Status.Value is JoinedRoomStatus); } [Test] @@ -130,14 +121,5 @@ namespace osu.Game.Tests.Visual.Multiplayer } private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; - - private void joinRequested(Room room) => room.Status.Value = new JoinedRoomStatus(); - - private class JoinedRoomStatus : RoomStatus - { - public override string Message => "Joined"; - - public override Color4 GetAppropriateColour(OsuColour colours) => colours.Yellow; - } } } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index b16b61c5c7..7bf161d1d0 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Playlists AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms.First())); - AddStep("select last room", () => roomsContainer.Rooms.Last().Action?.Invoke()); + AddStep("select last room", () => roomsContainer.Rooms.Last().Click()); AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms.First())); AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms.Last())); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index bc860772b7..678a97a04c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private BeatmapManager beatmaps { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private Bindable selectedRoom { get; set; } [Resolved(canBeNull: true)] From b4ca6b6188d90c4db0436a19ec57364b6942ac41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 14:53:12 +0900 Subject: [PATCH 087/489] Update popover logic to take advantage of new explicit popup functionality --- .../Lounge/Components/DrawableRoom.cs | 78 ++++++++++--------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 678a97a04c..1747dc6565 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -241,12 +242,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Alpha = 0; } + public Popover GetPopover() => new PasswordEntryPopover(Room); + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem("Create copy", MenuItemType.Standard, () => + { + parentScreen?.OpenNewRoom(Room.CreateCopy()); + }) + }; + public bool OnPressed(GlobalAction action) { + if (selectedRoom.Value != Room) + return false; + switch (action) { case GlobalAction.Select: - // TODO: this needs to be able to show the popover on demand. + Click(); return true; } @@ -259,6 +273,33 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; + protected override bool OnMouseDown(MouseDownEvent e) + { + if (selectedRoom.Value != Room) + return true; + + return base.OnMouseDown(e); + } + + protected override bool OnClick(ClickEvent e) + { + if (Room != selectedRoom.Value) + { + selectedRoom.Value = Room; + return true; + } + + if (Room.HasPassword.Value) + { + this.ShowPopover(); + return true; + } + + lounge?.Join(Room, null); + + return base.OnClick(e); + } + private class RoomName : OsuSpriteText { [Resolved(typeof(Room), nameof(Online.Rooms.Room.Name))] @@ -271,14 +312,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - public MenuItem[] ContextMenuItems => new MenuItem[] - { - new OsuMenuItem("Create copy", MenuItemType.Standard, () => - { - parentScreen?.OpenNewRoom(Room.CreateCopy()); - }) - }; - private class PasswordProtectedIcon : CompositeDrawable { [BackgroundDependencyLoader] @@ -312,33 +345,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - protected override bool OnMouseDown(MouseDownEvent e) - { - if (selectedRoom.Value != Room) - return true; - - return base.OnMouseDown(e); - } - - protected override bool OnClick(ClickEvent e) - { - if (Room != selectedRoom.Value) - { - selectedRoom.Value = Room; - return true; - } - - if (Room.HasPassword.Value) - // we want our popover to show. this is a bit of a hack. - return false; - - lounge?.Join(Room, null); - - return base.OnClick(e); - } - - public Popover GetPopover() => new PasswordEntryPopover(Room); - public class PasswordEntryPopover : Popover { [Resolved(canBeNull: true)] From 947460c3c54d46900385f3321ecfae0d8c55a855 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 15:49:09 +0900 Subject: [PATCH 088/489] Add test flow for joining passworded rooms via UI --- .../TestSceneMultiplayerLoungeSubScreen.cs | 35 ++++++++++++++++++- .../Visual/OnlinePlay/BasicTestRoomManager.cs | 8 ++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 0d90b75be7..3ba0f4969a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,12 +3,16 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Tests.Visual.OnlinePlay; +using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { @@ -18,6 +22,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private LoungeSubScreen loungeScreen; + private Room lastJoinedRoom; + private string lastJoinedPassword; + public override void SetUpSteps() { base.SetUpSteps(); @@ -25,20 +32,46 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("push screen", () => LoadScreen(loungeScreen = new MultiplayerLoungeSubScreen())); AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen()); + + AddStep("bind to event", () => + { + lastJoinedRoom = null; + lastJoinedPassword = null; + RoomManager.JoinRoomRequested = onRoomJoined; + }); } [Test] public void TestJoinRoomWithoutPassword() { AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room", () => InputManager.Key(Key.Enter)); + + AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First()); + AddAssert("room join password correct", () => lastJoinedPassword == null); } [Test] public void TestJoinRoomWithPassword() { + DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = loungeScreen.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + + AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First()); + AddAssert("room join password correct", () => lastJoinedPassword == "password"); } - private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First(); + private void onRoomJoined(Room room, string password) + { + lastJoinedRoom = room; + lastJoinedPassword = password; + } } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index 5d3fa34ec5..82c7266598 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -26,6 +26,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay public readonly BindableList Rooms = new BindableList(); + public Action JoinRoomRequested; + public IBindable InitialRoomsReceived { get; } = new Bindable(true); IBindableList IRoomManager.Rooms => Rooms; @@ -37,7 +39,11 @@ namespace osu.Game.Tests.Visual.OnlinePlay onSuccess?.Invoke(room); } - public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); + public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) + { + JoinRoomRequested?.Invoke(room, password); + onSuccess?.Invoke(room); + } public void PartRoom() { From 413f8adb36e36115deb020160acee13b8a1e6ec7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 15:53:28 +0900 Subject: [PATCH 089/489] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9280eaf97c..f9ec8dd099 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8a3c69e40c..8f9a57167f 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 2eea646c61..c6e52b8dd5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From bbc3a013c888596a8e4501033c9bd813112735bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 18:29:50 +0900 Subject: [PATCH 090/489] Use `BasicPopover` for now --- .../Lounge/Components/DrawableRoom.cs | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 1747dc6565..a95e0c2b16 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -345,50 +345,44 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - public class PasswordEntryPopover : Popover + public class PasswordEntryPopover : BasicPopover { + private readonly Room room; + [Resolved(canBeNull: true)] private LoungeSubScreen lounge { get; set; } public PasswordEntryPopover(Room room) + { + this.room = room; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) { OsuPasswordTextBox passwordTextbox; - Child = new Container + Child = new FillFlowContainer { + Margin = new MarginPadding(10), + Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, Children = new Drawable[] { - new Box + passwordTextbox = new OsuPasswordTextBox { - Colour = Color4.OliveDrab, - RelativeSizeAxes = Axes.Both, + Width = 200, }, - new FillFlowContainer + new TriangleButton { - Margin = new MarginPadding(10), - Spacing = new Vector2(5), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - passwordTextbox = new OsuPasswordTextBox - { - Width = 200, - }, - new TriangleButton - { - Width = 80, - Text = "Join Room", - Action = () => lounge?.Join(room, passwordTextbox.Text) - } - } - }, + Width = 80, + Text = "Join Room", + Action = () => lounge?.Join(room, passwordTextbox.Text) + } } }; } - - protected override Drawable CreateArrow() => Drawable.Empty(); } } } From c5319c06c2e29ddc1f1903ad48b1ecd7e832224b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 18:54:07 +0900 Subject: [PATCH 091/489] Add password attributes to `CopyFrom` to make testing work better --- osu.Game/Online/Rooms/Room.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 416dc7e5c0..f79a410bd9 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -159,6 +159,8 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; Availability.Value = other.Availability.Value; + Password.Value = other.Password.Value; + HasPassword.Value = other.HasPassword.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; ParticipantCount.Value = other.ParticipantCount.Value; From 4dea2d97782151a1a8e383cd09cd225662b99d15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 18:54:17 +0900 Subject: [PATCH 092/489] Dismiss popovers on returning to lounge --- .../Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 7 +++++++ osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 5a9721a8e3..8ab80d947c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -7,6 +7,7 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -244,5 +245,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (roomManager != null) roomManager.RoomsUpdated -= updateSorting; } + + public void HideAnyPopovers() + { + // must be called on a child of the PopoverContainer due to parent traversal not considering self. + roomFlow.HidePopover(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 11298037d0..dd6106b868 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -46,10 +46,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [CanBeNull] private IDisposable joiningRoomOperation { get; set; } + private RoomsContainer roomsContainer; + [BackgroundDependencyLoader] private void load() { - RoomsContainer roomsContainer; OsuScrollContainer scrollContainer; InternalChildren = new Drawable[] @@ -165,6 +166,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { base.OnSuspending(next); filter.HoldFocus = false; + + // ensure any password prompt is dismissed. + roomsContainer.HideAnyPopovers(); } public void Join(Room room, string password) From 125bd36ab161f683811d93d434ecbbcddc12f6ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 14:27:07 +0900 Subject: [PATCH 093/489] Send password in request ctor directly --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 7 +++++-- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 5 +---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index a82dc5a859..53bd2a6106 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -10,19 +10,22 @@ namespace osu.Game.Online.Rooms public class JoinRoomRequest : APIRequest { private readonly Room room; + private readonly string password; - public JoinRoomRequest(Room room) + public JoinRoomRequest(Room room, string password) { this.room = room; + this.password = password; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; + req.AddParameter("password", password); return req; } - protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}?password={room.Password.Value}"; + protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}"; } } diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index da02f9624f..422576648c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -86,11 +86,8 @@ namespace osu.Game.Screens.OnlinePlay.Components public virtual void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { - // todo: send into JoinRoomRequest directly? - room.Password.Value = password; - currentJoinRoomRequest?.Cancel(); - currentJoinRoomRequest = new JoinRoomRequest(room); + currentJoinRoomRequest = new JoinRoomRequest(room, password); currentJoinRoomRequest.Success += () => { From 6409a518dbba46a9f5f30f88f6b560442c9daa9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 16:34:56 +0900 Subject: [PATCH 094/489] Focus password text box on popover display --- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index a95e0c2b16..b8c4b57378 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -357,11 +357,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components this.room = room; } + private OsuPasswordTextBox passwordTextbox; + [BackgroundDependencyLoader] private void load(OsuColour colours) { - OsuPasswordTextBox passwordTextbox; - Child = new FillFlowContainer { Margin = new MarginPadding(10), @@ -383,6 +383,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + GetContainingInputManager().ChangeFocus(passwordTextbox); + } } } } From 481e4dedb0d7c49e0a2470af5645ceba3cb2d521 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 16:35:49 +0900 Subject: [PATCH 095/489] Move `PopoverContainer` to `OsuGameBase` --- osu.Game/OsuGameBase.cs | 7 ++++++- .../OnlinePlay/Lounge/Components/RoomsContainer.cs | 14 ++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5878727ad8..4b5fa4f62e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -13,6 +13,7 @@ using osu.Framework.Development; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -341,7 +342,11 @@ namespace osu.Game globalBindings = new GlobalActionContainer(this) }; - MenuCursorContainer.Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }; + MenuCursorContainer.Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both } + }; base.Content.Add(CreateScalingContainer().WithChildren(mainContent)); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 8ab80d947c..0910ff1a7a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Threading; @@ -51,21 +50,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new PopoverContainer + InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = new OsuContextMenuContainer + Child = roomFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = roomFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2), - } + Direction = FillDirection.Vertical, + Spacing = new Vector2(2), } }; } From e7b78b1ea5299dae24c07a16243f24ba4888d170 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 20:26:05 +0900 Subject: [PATCH 096/489] Adjust transform logic to hopefully be a bit easier to parse --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 27c352070a..8f9ff81808 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -31,6 +31,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -143,22 +144,24 @@ namespace osu.Game.Rulesets.Osu.Mods { if (!(drawable is DrawableHitCircle circle)) return; - var h = (OsuHitObject)drawable.HitObject; + double startTime = circle.HitObject.StartTime; + double preempt = circle.HitObject.TimePreempt; - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + using (drawable.BeginAbsoluteSequence(startTime - preempt)) { + // initial state drawable.ScaleTo(0.5f) - .Then().ScaleTo(1f, h.TimePreempt); + .FadeColour(OsuColour.Gray(0.5f)); - var colour = drawable.Colour; - - drawable.FadeColour(OsuColour.Gray(0.45f)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) - .FadeColour(colour, undim_duration); - - // Remove approach circles - circle.ApproachCircle.Hide(); + // scale to final size + drawable.ScaleTo(1f, preempt); } + + using (drawable.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) + drawable.FadeColour(Color4.White, undim_duration); + + // Remove approach circles + circle.ApproachCircle.Hide(); } #endregion From 9bec53bfa8922bc7de66278a1f0c30e1ba65eb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Jun 2021 19:31:24 +0200 Subject: [PATCH 097/489] Implement osu!-side popover --- .../UserInterface/TestSceneOsuPopover.cs | 103 ++++++++++++++++++ .../Graphics/UserInterfaceV2/OsuPopover.cs | 44 ++++++++ 2 files changed, 147 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs new file mode 100644 index 0000000000..1848cf6a5e --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs @@ -0,0 +1,103 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneOsuPopover : OsuGridTestScene + { + public TestSceneOsuPopover() + : base(1, 2) + { + Cell(0, 0).Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"No OverlayColourProvider", + Font = OsuFont.Default.With(size: 40) + }, + new TriangleButtonWithPopover() + } + }; + + Cell(0, 1).Child = new ColourProvidingContainer(OverlayColourScheme.Orange) + { + RelativeSizeAxes = Axes.Both, + Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"With OverlayColourProvider (orange)", + Font = OsuFont.Default.With(size: 40) + }, + new TriangleButtonWithPopover() + } + } + }; + } + + private class TriangleButtonWithPopover : TriangleButton, IHasPopover + { + public TriangleButtonWithPopover() + { + Width = 100; + Height = 30; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Text = @"open"; + Action = this.ShowPopover; + } + + public Popover GetPopover() => new OsuPopover + { + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"sample text" + }, + new OsuTextBox + { + Width = 150, + Height = 30 + } + } + } + }; + } + + private class ColourProvidingContainer : Container + { + [Cached] + private OverlayColourProvider provider { get; } + + public ColourProvidingContainer(OverlayColourScheme colourScheme) + { + provider = new OverlayColourProvider(colourScheme); + } + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs new file mode 100644 index 0000000000..33165ea8b5 --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class OsuPopover : Popover + { + private const float fade_duration = 250; + + public OsuPopover(bool withPadding = true) + { + Content.Padding = withPadding ? new MarginPadding(20) : new MarginPadding(); + Body.Masking = true; + Body.CornerRadius = 10; + Body.Margin = new MarginPadding(10); + Body.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0, 2), + Radius = 5, + Colour = Colour4.Black.Opacity(0.3f) + }; + } + + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour osuColour) + { + Background.Colour = Arrow.Colour = colourProvider?.Background4 ?? osuColour.GreySeafoamDarker; + } + + protected override Drawable CreateArrow() => Empty(); + + protected override void PopIn() => this.FadeIn(fade_duration, Easing.OutQuint); + protected override void PopOut() => this.FadeOut(fade_duration, Easing.OutQuint); + } +} From 7f432665e52774a5bef21cf930ff2ac1ac0527f6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 14 Jul 2021 14:38:38 +0900 Subject: [PATCH 098/489] Preserve Y position of hit objects in osu!catch --- .../Beatmaps/CatchBeatmapConverter.cs | 11 +++++---- .../Objects/CatchHitObject.cs | 24 ++++++++++++++++--- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 5 +--- .../Objects/Legacy/Catch/ConvertHit.cs | 9 +++++-- .../Legacy/Catch/ConvertHitObjectParser.cs | 4 ++-- .../Objects/Legacy/Catch/ConvertSlider.cs | 9 +++++-- 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 34964fc4ae..7774a7da09 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps protected override IEnumerable ConvertHitObject(HitObject obj, IBeatmap beatmap, CancellationToken cancellationToken) { - var positionData = obj as IHasXPosition; + var xPositionData = obj as IHasXPosition; + var yPositionData = obj as IHasYPosition; var comboData = obj as IHasCombo; switch (obj) @@ -36,10 +37,11 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, - X = positionData?.X ?? 0, + X = xPositionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, - LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 + LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0, + LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); case IHasDuration endTime: @@ -59,7 +61,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, - X = positionData?.X ?? 0 + X = xPositionData?.X ?? 0, + LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 0b8c0e28a7..f979e3e0ca 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -9,10 +9,11 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Rulesets.Catch.Objects { - public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation + public abstract class CatchHitObject : HitObject, IHasPosition, IHasComboInformation { public const float OBJECT_RADIUS = 64; @@ -31,8 +32,6 @@ namespace osu.Game.Rulesets.Catch.Objects set => OriginalXBindable.Value = value; } - float IHasXPosition.X => OriginalXBindable.Value; - public readonly Bindable XOffsetBindable = new Bindable(); /// @@ -131,5 +130,24 @@ namespace osu.Game.Rulesets.Catch.Objects } protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + #region Hit object conversion + + // The half of the height of the osu! playfield. + public const float DEFAULT_LEGACY_CONVERT_Y = 192; + + /// + /// The Y position of the hit object is not used in the normal osu!catch gameplay. + /// It is preserved to maximize the backward compatibility with the legacy editor, in which the mappers use the Y position to organize the patterns. + /// + public float LegacyConvertedY { get; set; } = DEFAULT_LEGACY_CONVERT_Y; + + float IHasXPosition.X => OriginalX; + + float IHasYPosition.Y => LegacyConvertedY; + + Vector2 IHasPosition.Position => new Vector2(OriginalX, LegacyConvertedY); + + #endregion } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index acbf57d25f..f14f6ec10c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -251,11 +251,8 @@ namespace osu.Game.Beatmaps.Formats switch (beatmap.BeatmapInfo.RulesetID) { case 0: - position = ((IHasPosition)hitObject).Position; - break; - case 2: - position.X = ((IHasXPosition)hitObject).X; + position = ((IHasPosition)hitObject).Position; break; case 3: diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 19722fb796..12b4812824 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -2,15 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Catch { /// /// Legacy osu!catch Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasCombo, IHasXPosition + internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo { - public float X { get; set; } + public float X => Position.X; + + public float Y => Position.Y; + + public Vector2 Position { get; set; } public bool NewCombo { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index c10c8dc30f..c29179f749 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch return new ConvertHit { - X = position.X, + Position = position, NewCombo = newCombo, ComboOffset = comboOffset }; @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch return new ConvertSlider { - X = position.X, + Position = position, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index 56790629b4..fb1afed3b4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -2,15 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Catch { /// /// Legacy osu!catch Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo { - public float X { get; set; } + public float X => Position.X; + + public float Y => Position.Y; + + public Vector2 Position { get; set; } public bool NewCombo { get; set; } From fdfd82aec42256ca1f44ab3b576c5c410640316d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:23:44 +0900 Subject: [PATCH 099/489] Add elastic scale on appear --- osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index 33165ea8b5..fff18e615f 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -14,10 +14,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 public class OsuPopover : Popover { private const float fade_duration = 250; + private const double scale_duration = 500; public OsuPopover(bool withPadding = true) { Content.Padding = withPadding ? new MarginPadding(20) : new MarginPadding(); + Body.Masking = true; Body.CornerRadius = 10; Body.Margin = new MarginPadding(10); @@ -38,7 +40,16 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override Drawable CreateArrow() => Empty(); - protected override void PopIn() => this.FadeIn(fade_duration, Easing.OutQuint); - protected override void PopOut() => this.FadeOut(fade_duration, Easing.OutQuint); + protected override void PopIn() + { + this.ScaleTo(1, scale_duration, Easing.OutElasticHalf); + this.FadeIn(fade_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + this.ScaleTo(0.7f, scale_duration, Easing.OutQuint); + this.FadeOut(fade_duration, Easing.OutQuint); + } } } From c6116676ebb9e090a82717537e09027399009a9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:23:48 +0900 Subject: [PATCH 100/489] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index cd57d7478e..171a0862a1 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index eb7a0141c7..b2f1d6507f 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 2e5fab758d..dc15df6ea6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 339fab75a83a56df8124e5794170d9caaf7976bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:27:03 +0900 Subject: [PATCH 101/489] Rename colour variable in line with other usages --- osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index fff18e615f..c07a5de1e4 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -33,9 +33,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 } [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour osuColour) + private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour colours) { - Background.Colour = Arrow.Colour = colourProvider?.Background4 ?? osuColour.GreySeafoamDarker; + Background.Colour = Arrow.Colour = colourProvider?.Background4 ?? colours.GreySeafoamDarker; } protected override Drawable CreateArrow() => Empty(); From cc09a8b5badf002dedaa80bd0b57cb94e9abff75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:55:46 +0900 Subject: [PATCH 102/489] Update to use `OsuPopover` implementation --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index b8c4b57378..7f4ea0908d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -23,6 +23,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; @@ -345,7 +346,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - public class PasswordEntryPopover : BasicPopover + public class PasswordEntryPopover : OsuPopover { private readonly Room room; From 9d693c75cfad98bd011bf0f75427f21372994c38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:56:52 +0900 Subject: [PATCH 103/489] Add `Schedule` to restore password text box focus behaviour --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 7f4ea0908d..84c20a6acc 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -389,7 +389,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - GetContainingInputManager().ChangeFocus(passwordTextbox); + Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); } } } From 1041a5fb940ad95775853c5ed56fe41309ba1c8e Mon Sep 17 00:00:00 2001 From: Evan Boehs <51836263+boehs@users.noreply.github.com> Date: Wed, 14 Jul 2021 14:39:45 -0400 Subject: [PATCH 104/489] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e95c12cfdc..016bd7d922 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ We are accepting bug reports (please report with as much detail as possible and - Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer). - You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management). -- Read peppy's [latest blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where the project is currently and the roadmap going forward. +- Read peppy's [blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where the project is currently and the roadmap going forward. ## Running osu! From 687c9aa33d44de87989fd5f9f1a9359c5bcc2880 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 12:37:52 +0900 Subject: [PATCH 105/489] Add tooltip and keywords for "high precision" setting --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index e87572e2ca..eefe160b94 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -47,7 +47,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input new SettingsCheckbox { LabelText = "High precision mouse", - Current = relativeMode + TooltipText = "Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as \"Raw Input\".", + Current = relativeMode, + Keywords = new[] { "raw", "input", "relative", "cursor" } }, new SensitivitySetting { From 5b91111edabdfa7e1b7b2123c5322d73ffdd48ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 12:50:34 +0900 Subject: [PATCH 106/489] Update `SettingsSubsection.Header` to accept a `LocalisableString` --- .../ManiaSettingsSubsection.cs | 2 +- .../UI/OsuSettingsSubsection.cs | 3 ++- osu.Game/Localisation/MouseSettingsStrings.cs | 19 +++++++++++++++++++ .../KeyBinding/GlobalKeyBindingsSection.cs | 11 ++++++----- .../KeyBinding/VariantBindingsSubsection.cs | 3 ++- .../Sections/Audio/AudioDevicesSettings.cs | 2 +- .../Settings/Sections/Audio/OffsetSettings.cs | 2 +- .../Settings/Sections/Audio/VolumeSettings.cs | 3 ++- .../Sections/Debug/GeneralSettings.cs | 3 ++- .../Settings/Sections/Debug/MemorySettings.cs | 3 ++- .../Sections/Gameplay/GeneralSettings.cs | 3 ++- .../Sections/Gameplay/ModsSettings.cs | 3 ++- .../Sections/General/LanguageSettings.cs | 3 ++- .../Sections/General/UpdateSettings.cs | 3 ++- .../Sections/Graphics/DetailSettings.cs | 3 ++- .../Sections/Graphics/LayoutSettings.cs | 2 +- .../Sections/Graphics/RendererSettings.cs | 3 ++- .../Sections/Input/BindingSettings.cs | 3 ++- .../Settings/Sections/Input/MouseSettings.cs | 3 ++- .../Settings/Sections/Input/TabletSettings.cs | 3 ++- .../Settings/Sections/InputSection.cs | 3 ++- .../Sections/Maintenance/GeneralSettings.cs | 3 ++- .../Online/AlertsAndPrivacySettings.cs | 3 ++- .../Sections/Online/IntegrationSettings.cs | 3 ++- .../Settings/Sections/Online/WebSettings.cs | 3 ++- .../Sections/UserInterface/GeneralSettings.cs | 2 +- .../UserInterface/MainMenuSettings.cs | 3 ++- .../UserInterface/SongSelectSettings.cs | 2 +- .../Overlays/Settings/SettingsSubsection.cs | 7 ++++--- 29 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Localisation/MouseSettingsStrings.cs diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs index f89750a96e..36fa336d0c 100644 --- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs +++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaSettingsSubsection : RulesetSettingsSubsection { - protected override string Header => "osu!mania"; + protected override LocalisableString Header => "osu!mania"; public ManiaSettingsSubsection(ManiaRuleset ruleset) : base(ruleset) diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 705ba3e929..a4c0381d16 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.UI; @@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuSettingsSubsection : RulesetSettingsSubsection { - protected override string Header => "osu!"; + protected override LocalisableString Header => "osu!"; public OsuSettingsSubsection(Ruleset ruleset) : base(ruleset) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs new file mode 100644 index 0000000000..0260def409 --- /dev/null +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -0,0 +1,19 @@ +// 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 MouseSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.MouseSettings"; + + /// + /// "Mouse" + /// + public static LocalisableString Mouse => new TranslatableString(getKey(@"mouse"), @"Mouse"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index c905397e77..6ea4209cce 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings; @@ -28,7 +29,7 @@ namespace osu.Game.Overlays.KeyBinding private class DefaultBindingsSubsection : KeyBindingsSubsection { - protected override string Header => string.Empty; + protected override LocalisableString Header => string.Empty; public DefaultBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -39,7 +40,7 @@ namespace osu.Game.Overlays.KeyBinding private class SongSelectKeyBindingSubsection : KeyBindingsSubsection { - protected override string Header => "Song Select"; + protected override LocalisableString Header => "Song Select"; public SongSelectKeyBindingSubsection(GlobalActionContainer manager) : base(null) @@ -50,7 +51,7 @@ namespace osu.Game.Overlays.KeyBinding private class InGameKeyBindingsSubsection : KeyBindingsSubsection { - protected override string Header => "In Game"; + protected override LocalisableString Header => "In Game"; public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -61,7 +62,7 @@ namespace osu.Game.Overlays.KeyBinding private class AudioControlKeyBindingsSubsection : KeyBindingsSubsection { - protected override string Header => "Audio"; + protected override LocalisableString Header => "Audio"; public AudioControlKeyBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -72,7 +73,7 @@ namespace osu.Game.Overlays.KeyBinding private class EditorKeyBindingsSubsection : KeyBindingsSubsection { - protected override string Header => "Editor"; + protected override LocalisableString Header => "Editor"; public EditorKeyBindingsSubsection(GlobalActionContainer manager) : base(null) diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs index 861d59c8f4..7618a42282 100644 --- a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs @@ -1,13 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; using osu.Game.Rulesets; namespace osu.Game.Overlays.KeyBinding { public class VariantBindingsSubsection : KeyBindingsSubsection { - protected override string Header { get; } + protected override LocalisableString Header { get; } public VariantBindingsSubsection(RulesetInfo ruleset, int variant) : base(variant) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs index b31e7dc45b..d64f176468 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { public class AudioDevicesSettings : SettingsSubsection { - protected override string Header => "Devices"; + protected override LocalisableString Header => "Devices"; [Resolved] private AudioManager audio { get; set; } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 1ae297f2a9..7f2e377c83 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { public class OffsetSettings : SettingsSubsection { - protected override string Header => "Offset Adjustment"; + protected override LocalisableString Header => "Offset Adjustment"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs index c172a76ab9..8f88b03471 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs @@ -4,13 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Audio { public class VolumeSettings : SettingsSubsection { - protected override string Header => "Volume"; + protected override LocalisableString Header => "Volume"; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index 4a9c9bd8a2..2b868cab85 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Screens.Import; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; [BackgroundDependencyLoader(true)] private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs index db64c9a8ac..bf7fb351c0 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs @@ -4,13 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Framework.Platform; namespace osu.Game.Overlays.Settings.Sections.Debug { public class MemorySettings : SettingsSubsection { - protected override string Header => "Memory"; + protected override LocalisableString Header => "Memory"; [BackgroundDependencyLoader] private void load(FrameworkDebugConfigManager config, GameHost host) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 0b5ec4f338..353292606f 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -4,6 +4,7 @@ using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index 2b2fb9cef7..ec9ddde2da 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -4,13 +4,14 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Gameplay { public class ModsSettings : SettingsSubsection { - protected override string Header => "Mods"; + protected override LocalisableString Header => "Mods"; public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "mod" }); diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index dfcdb8e340..c6c752e2fd 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Extensions; using osu.Game.Localisation; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsDropdown languageSelection; private Bindable frameworkLocale; - protected override string Header => "Language"; + protected override LocalisableString Header => "Language"; [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index c213313559..dd20e1d7ef 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Configuration; @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.General [Resolved(CanBeNull = true)] private UpdateManager updateManager { get; set; } - protected override string Header => "Updates"; + protected override LocalisableString Header => "Updates"; private SettingsButton checkForUpdatesButton; diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index 30caa45995..f889cfca0f 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Graphics { public class DetailSettings : SettingsSubsection { - protected override string Header => "Detail Settings"; + protected override LocalisableString Header => "Detail Settings"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 669753d2cb..91208cb78a 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { public class LayoutSettings : SettingsSubsection { - protected override string Header => "Layout"; + protected override LocalisableString Header => "Layout"; private FillFlowContainer> scalingSettings; diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 70225ff6b8..2210c7911e 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Configuration; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { public class RendererSettings : SettingsSubsection { - protected override string Header => "Renderer"; + protected override LocalisableString Header => "Renderer"; private SettingsEnumDropdown frameLimiterDropdown; diff --git a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs index 79c73863cf..e68cd606e5 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs @@ -2,12 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { public class BindingSettings : SettingsSubsection { - protected override string Header => "Shortcut and gameplay bindings"; + protected override LocalisableString Header => "Shortcut and gameplay bindings"; public BindingSettings(KeyBindingPanel keyConfig) { diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index eefe160b94..b39507da62 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -10,6 +10,7 @@ using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Input; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { private readonly MouseHandler mouseHandler; - protected override string Header => "Mouse"; + protected override LocalisableString Header => MouseSettingsStrings.Mouse; private Bindable handlerSensitivity; diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index d770c18878..e9ed3378a2 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; @@ -52,7 +53,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private OsuSpriteText noTabletMessage; - protected override string Header => "Tablet"; + protected override LocalisableString Header => "Tablet"; public TabletSettings(ITabletHandler tabletHandler) { diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index 6e99891794..366f39388a 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -9,6 +9,7 @@ using osu.Framework.Input.Handlers.Joystick; using osu.Framework.Input.Handlers.Midi; using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Overlays.Settings.Sections.Input; @@ -100,7 +101,7 @@ namespace osu.Game.Overlays.Settings.Sections }; } - protected override string Header => handler.Description; + protected override LocalisableString Header => handler.Description; } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index a38ca81e23..b9a408b1f8 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Database; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; private TriangleButton importBeatmapsButton; private TriangleButton importScoresButton; diff --git a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs index b0f6400d4f..3a2de2ee36 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Online { public class AlertsAndPrivacySettings : SettingsSubsection { - protected override string Header => "Alerts and Privacy"; + protected override LocalisableString Header => "Alerts and Privacy"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs index d2867962c0..f2012f0d9c 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Online { public class IntegrationSettings : SettingsSubsection { - protected override string Header => "Integrations"; + protected override LocalisableString Header => "Integrations"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index 59bcbe4d89..89e7b096f3 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Online { public class WebSettings : SettingsSubsection { - protected override string Header => "Web"; + protected override LocalisableString Header => "Web"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index a6eb008623..4b26645ef3 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 5f703ed5a4..81bbcbb54a 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Users; @@ -12,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { public class MainMenuSettings : SettingsSubsection { - protected override string Header => "Main Menu"; + protected override LocalisableString Header => "Main Menu"; private IBindable user; diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 2470c0a6c5..587155eb0d 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface private Bindable minStars; private Bindable maxStars; - protected override string Header => "Song Select"; + protected override LocalisableString Header => "Song Select"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index 6abf6283b9..df32424b67 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -8,6 +8,7 @@ using osu.Game.Graphics.Sprites; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Graphics; @@ -20,10 +21,10 @@ namespace osu.Game.Overlays.Settings protected readonly FillFlowContainer FlowContent; - protected abstract string Header { get; } + protected abstract LocalisableString Header { get; } public IEnumerable FilterableChildren => Children.OfType(); - public virtual IEnumerable FilterTerms => new[] { Header }; + public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; public bool MatchingFilter { @@ -54,7 +55,7 @@ namespace osu.Game.Overlays.Settings { new OsuSpriteText { - Text = Header.ToUpperInvariant(), + Text = Header.ToString().ToUpper(), // TODO: Add localisation support after https://github.com/ppy/osu-framework/pull/4603 is merged. Margin = new MarginPadding { Vertical = 30, Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS }, Font = OsuFont.GetFont(weight: FontWeight.Bold), }, From d58534dce60161c0207d27c160306dc1267021d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 13:01:21 +0900 Subject: [PATCH 107/489] Update `CommonStrings` to latest localiser tools output --- osu.Game/Localisation/CommonStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 50e01f06fc..ad87986554 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -14,6 +14,6 @@ namespace osu.Game.Localisation /// public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel"); - private static string getKey(string key) => $"{prefix}:{key}"; + private static string getKey(string key) => $@"{prefix}:{key}"; } } From 3f3adfe2979ac732bc2689b63d9066450a12833f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 13:07:35 +0900 Subject: [PATCH 108/489] Add localisation support for `MouseSettings` --- osu.Game/Localisation/MouseSettingsStrings.cs | 42 ++++++++++++++++++- .../Settings/Sections/Input/MouseSettings.cs | 18 ++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index 0260def409..9b1f7fe4c5 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -14,6 +14,46 @@ namespace osu.Game.Localisation /// public static LocalisableString Mouse => new TranslatableString(getKey(@"mouse"), @"Mouse"); + /// + /// "Not applicable in full screen mode" + /// + public static LocalisableString NotApplicableFullscreen => new TranslatableString(getKey(@"not_applicable_full_screen"), @"Not applicable in full screen mode"); + + /// + /// "High precision mouse" + /// + public static LocalisableString HighPrecisionMouse => new TranslatableString(getKey(@"high_precision_mouse"), @"High precision mouse"); + + /// + /// "Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as "Raw Input"." + /// + public static LocalisableString HighPrecisionMouseTooltip => new TranslatableString(getKey(@"high_precision_mouse_tooltip"), @"Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as ""Raw Input""."); + + /// + /// "Confine mouse cursor to window" + /// + public static LocalisableString ConfineMouseMode => new TranslatableString(getKey(@"confine_mouse_mode"), @"Confine mouse cursor to window"); + + /// + /// "Disable mouse wheel during gameplay" + /// + public static LocalisableString DisableMouseWheel => new TranslatableString(getKey(@"disable_mouse_wheel"), @"Disable mouse wheel during gameplay"); + + /// + /// "Disable mouse buttons during gameplay" + /// + public static LocalisableString DisableMouseButtons => new TranslatableString(getKey(@"disable_mouse_buttons"), @"Disable mouse buttons during gameplay"); + + /// + /// "Enable high precision mouse to adjust sensitivity" + /// + public static LocalisableString EnableHighPrecisionForSensitivityAdjust => new TranslatableString(getKey(@"enable_high_precision_for_sensitivity_adjust"), @"Enable high precision mouse to adjust sensitivity"); + + /// + /// "Cursor sensitivity" + /// + public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index b39507da62..753096a207 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -47,29 +47,29 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsCheckbox { - LabelText = "High precision mouse", - TooltipText = "Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as \"Raw Input\".", + LabelText = MouseSettingsStrings.HighPrecisionMouse, + TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip, Current = relativeMode, - Keywords = new[] { "raw", "input", "relative", "cursor" } + Keywords = new[] { @"raw", @"input", @"relative", @"cursor" } }, new SensitivitySetting { - LabelText = "Cursor sensitivity", + LabelText = MouseSettingsStrings.CursorSensitivity, Current = localSensitivity }, confineMouseModeSetting = new SettingsEnumDropdown { - LabelText = "Confine mouse cursor to window", + LabelText = MouseSettingsStrings.ConfineMouseMode, Current = osuConfig.GetBindable(OsuSetting.ConfineMouseMode) }, new SettingsCheckbox { - LabelText = "Disable mouse wheel during gameplay", + LabelText = MouseSettingsStrings.DisableMouseWheel, Current = osuConfig.GetBindable(OsuSetting.MouseDisableWheel) }, new SettingsCheckbox { - LabelText = "Disable mouse buttons during gameplay", + LabelText = MouseSettingsStrings.DisableMouseButtons, Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons) }, }; @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (isFullscreen) { confineMouseModeSetting.Current.Disabled = true; - confineMouseModeSetting.TooltipText = "Not applicable in full screen mode"; + confineMouseModeSetting.TooltipText = MouseSettingsStrings.NotApplicableFullscreen; } else { @@ -120,7 +120,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class SensitivitySlider : OsuSliderBar { - public override LocalisableString TooltipText => Current.Disabled ? "enable high precision mouse to adjust sensitivity" : $"{base.TooltipText}x"; + public override LocalisableString TooltipText => Current.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $"{base.TooltipText}x"; } } } From 8cc2d2e79e6679c3c541b6999bc1b8dbdd332f1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 13:51:28 +0900 Subject: [PATCH 109/489] Update beat synced container tests to be usable --- .../TestSceneBeatSyncedContainer.cs | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 82b7e65c4f..c952163ba5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -5,18 +5,18 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; +using osu.Game.Rulesets.Osu; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -24,37 +24,55 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneBeatSyncedContainer : OsuTestScene { - private readonly NowPlayingOverlay np; + private BeatContainer beatContainer; + private DecoupleableInterpolatingFramedClock decoupledClock; - public TestSceneBeatSyncedContainer() + [SetUpSteps] + public void SetUpSteps() { - Clock = new FramedClock(); - Clock.ProcessFrame(); - - AddRange(new Drawable[] + AddStep("Set beatmap", () => { - new BeatContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }, - np = new NowPlayingOverlay - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - } + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); }); + + AddStep("Create beat sync container", () => + { + Children = new Drawable[] + { + beatContainer = new BeatContainer + { + Clock = decoupledClock = new DecoupleableInterpolatingFramedClock + { + IsCoupled = false, + }, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, + }; + + decoupledClock.ChangeSource(Beatmap.Value.Track); + }); + + AddStep("Start playback", () => decoupledClock.Start()); } - protected override void LoadComplete() + [Test] + public void TestFirstBeatAtFirstTimingPoint() { - base.LoadComplete(); - np.ToggleVisibility(); + AddStep("Set time before zero", () => + { + decoupledClock.Seek(-1000); + }); + + AddStep("bind event", () => + { + beatContainer.NewBeat = (i, point, effectControlPoint, channelAmplitudes) => { }; + }); } private class BeatContainer : BeatSyncedContainer { - private const int flash_layer_heigth = 150; + private const int flash_layer_height = 150; private readonly InfoString timingPointCount; private readonly InfoString currentTimingPoint; @@ -64,12 +82,10 @@ namespace osu.Game.Tests.Visual.UserInterface private readonly InfoString adjustedBeatLength; private readonly InfoString timeUntilNextBeat; private readonly InfoString timeSinceLastBeat; + private readonly InfoString currentTime; private readonly Box flashLayer; - [Resolved] - private MusicController musicController { get; set; } - public BeatContainer() { RelativeSizeAxes = Axes.X; @@ -82,7 +98,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Bottom = flash_layer_heigth }, + Margin = new MarginPadding { Bottom = flash_layer_height }, Children = new Drawable[] { new Box @@ -98,6 +114,7 @@ namespace osu.Game.Tests.Visual.UserInterface Direction = FillDirection.Vertical, Children = new Drawable[] { + currentTime = new InfoString(@"Current time"), timingPointCount = new InfoString(@"Timing points amount"), currentTimingPoint = new InfoString(@"Current timing point"), beatCount = new InfoString(@"Beats amount (in the current timing point)"), @@ -116,7 +133,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, - Height = flash_layer_heigth, + Height = flash_layer_height, Children = new Drawable[] { new Box @@ -164,7 +181,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (timingPoints.Count == 0) return 0; if (timingPoints[^1] == current) - return (int)Math.Ceiling((musicController.CurrentTrack.Length - current.Time) / current.BeatLength); + return (int)Math.Ceiling((Clock.CurrentTime - current.Time) / current.BeatLength); return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); } @@ -174,8 +191,11 @@ namespace osu.Game.Tests.Visual.UserInterface base.Update(); timeUntilNextBeat.Value = TimeUntilNextBeat; timeSinceLastBeat.Value = TimeSinceLastBeat; + currentTime.Value = Clock.CurrentTime; } + public Action NewBeat; + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); @@ -187,7 +207,9 @@ namespace osu.Game.Tests.Visual.UserInterface beatsPerMinute.Value = 60000 / timingPoint.BeatLength; adjustedBeatLength.Value = timingPoint.BeatLength; - flashLayer.FadeOutFromOne(timingPoint.BeatLength); + flashLayer.FadeOutFromOne(timingPoint.BeatLength / 4); + + NewBeat?.Invoke(beatIndex, timingPoint, effectPoint, amplitudes); } } @@ -200,7 +222,7 @@ namespace osu.Game.Tests.Visual.UserInterface public double Value { - set => valueText.Text = $"{value:G}"; + set => valueText.Text = $"{value:0.##}"; } public InfoString(string header) From a3129ad00e22dc44c9f4965f8ef0702b22ce6a91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:30:38 +0900 Subject: [PATCH 110/489] Refactor `BeatSyncedContainer` to support `GameplayClock` --- .../TestSceneBeatSyncedContainer.cs | 35 ++++++----- .../Containers/BeatSyncedContainer.cs | 62 ++++++++++++++----- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index c952163ba5..f58f9fcaaf 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -11,12 +11,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; -using osu.Framework.Timing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Play; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -25,7 +25,8 @@ namespace osu.Game.Tests.Visual.UserInterface public class TestSceneBeatSyncedContainer : OsuTestScene { private BeatContainer beatContainer; - private DecoupleableInterpolatingFramedClock decoupledClock; + + private MasterGameplayClockContainer gameplayClockContainer; [SetUpSteps] public void SetUpSteps() @@ -39,21 +40,18 @@ namespace osu.Game.Tests.Visual.UserInterface { Children = new Drawable[] { - beatContainer = new BeatContainer + gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, 0) { - Clock = decoupledClock = new DecoupleableInterpolatingFramedClock + Child = beatContainer = new BeatContainer { - IsCoupled = false, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, }, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }, + } }; - - decoupledClock.ChangeSource(Beatmap.Value.Track); }); - AddStep("Start playback", () => decoupledClock.Start()); + AddStep("Start playback", () => gameplayClockContainer.Start()); } [Test] @@ -61,7 +59,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("Set time before zero", () => { - decoupledClock.Seek(-1000); + gameplayClockContainer.Seek(-1000); }); AddStep("bind event", () => @@ -150,8 +148,13 @@ namespace osu.Game.Tests.Visual.UserInterface } } }; + } - Beatmap.ValueChanged += delegate + protected override void LoadComplete() + { + base.LoadComplete(); + + Beatmap.BindValueChanged(_ => { timingPointCount.Value = 0; currentTimingPoint.Value = 0; @@ -161,7 +164,7 @@ namespace osu.Game.Tests.Visual.UserInterface adjustedBeatLength.Value = 0; timeUntilNextBeat.Value = 0; timeSinceLastBeat.Value = 0; - }; + }, true); } private List timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ToList(); @@ -181,7 +184,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (timingPoints.Count == 0) return 0; if (timingPoints[^1] == current) - return (int)Math.Ceiling((Clock.CurrentTime - current.Time) / current.BeatLength); + return (int)Math.Ceiling((BeatSyncClock.CurrentTime - current.Time) / current.BeatLength); return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); } @@ -191,7 +194,7 @@ namespace osu.Game.Tests.Visual.UserInterface base.Update(); timeUntilNextBeat.Value = TimeUntilNextBeat; timeSinceLastBeat.Value = TimeSinceLastBeat; - currentTime.Value = Clock.CurrentTime; + currentTime.Value = BeatSyncClock.CurrentTime; } public Action NewBeat; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index e2a0c09a6b..cb26406d64 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -1,19 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Screens.Play; namespace osu.Game.Graphics.Containers { + /// + /// A container which fires a callback when a new beat is reached. + /// Consumes a parent or (whichever is first available). + /// + /// + /// This container does not set its own clock to the source used for beat matching. + /// This means that if the beat source clock is playing faster or slower, animations may unexpectedly overlap. + /// Make sure this container's Clock is also set to the expected source (or within a parent element which provides this). + /// public class BeatSyncedContainer : Container { - protected readonly IBindable Beatmap = new Bindable(); - private int lastBeat; private TimingControlPoint lastTimingPoint; @@ -45,15 +55,45 @@ namespace osu.Game.Graphics.Containers protected bool IsBeatSyncedWithTrack { get; private set; } + protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + } + + [Resolved] + protected IBindable Beatmap { get; private set; } + + [Resolved(canBeNull: true)] + protected GameplayClock GameplayClock { get; private set; } + + protected IClock BeatSyncClock + { + get + { + if (GameplayClock != null) + return GameplayClock; + + if (Beatmap.Value.TrackLoaded) + return Beatmap.Value.Track; + + return null; + } + } + protected override void Update() { ITrack track = null; IBeatmap beatmap = null; double currentTrackTime = 0; + TimingControlPoint timingPoint = null; EffectControlPoint effectPoint = null; + var clock = BeatSyncClock; + + if (clock == null) + return; + if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) { track = Beatmap.Value.Track; @@ -62,7 +102,7 @@ namespace osu.Game.Graphics.Containers if (track != null && beatmap != null && track.IsRunning && track.Length > 0) { - currentTrackTime = track.CurrentTime + EarlyActivationMilliseconds; + currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); @@ -70,13 +110,15 @@ namespace osu.Game.Graphics.Containers IsBeatSyncedWithTrack = timingPoint?.BeatLength > 0; - if (timingPoint == null || !IsBeatSyncedWithTrack) + if (!IsBeatSyncedWithTrack) { - currentTrackTime = Clock.CurrentTime; + currentTrackTime = clock.CurrentTime; timingPoint = TimingControlPoint.DEFAULT; effectPoint = EffectControlPoint.DEFAULT; } + Debug.Assert(timingPoint != null); + double beatLength = timingPoint.BeatLength / Divisor; while (beatLength < MinimumBeatLength) @@ -103,15 +145,5 @@ namespace osu.Game.Graphics.Containers lastBeat = beatIndex; lastTimingPoint = timingPoint; } - - [BackgroundDependencyLoader] - private void load(IBindable beatmap) - { - Beatmap.BindTo(beatmap); - } - - protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - } } } From cab8b941322b2731605778a914badf9388e5b67c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:36:37 +0900 Subject: [PATCH 111/489] Add failing test --- .../TestSceneBeatSyncedContainer.cs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index f58f9fcaaf..ac1f88ad33 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -57,15 +58,28 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestFirstBeatAtFirstTimingPoint() { - AddStep("Set time before zero", () => - { - gameplayClockContainer.Seek(-1000); - }); + int? lastBeatIndex = null; + double? lastBpm = null; AddStep("bind event", () => { - beatContainer.NewBeat = (i, point, effectControlPoint, channelAmplitudes) => { }; + beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => + { + lastBeatIndex = i; + lastBpm = timingControlPoint.BPM; + }; }); + + AddStep("Set time before zero", () => + { + lastBeatIndex = null; + lastBpm = null; + gameplayClockContainer.Seek(-1000); + }); + + AddUntilStep("wait for trigger", () => lastBpm != null); + AddAssert("bpm is from beatmap", () => lastBpm != null&&Precision.AlmostEquals(lastBpm.Value, 128)); + AddAssert("beat index is less than zero", () => lastBeatIndex < 0); } private class BeatContainer : BeatSyncedContainer From 5ecf6511e6543300fdba03197b5ec53583db54be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:37:08 +0900 Subject: [PATCH 112/489] Fix default timing points being used if "track" is not running --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index cb26406d64..6d5f4d63ac 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -100,7 +100,7 @@ namespace osu.Game.Graphics.Containers beatmap = Beatmap.Value.Beatmap; } - if (track != null && beatmap != null && track.IsRunning && track.Length > 0) + if (track != null && beatmap != null && clock.IsRunning && track.Length > 0) { currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; From c47ff1919c0075cb64b7e1539885f26b6adc012a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:56:49 +0900 Subject: [PATCH 113/489] Fix regression in idle behaviour and refactor further --- .../Containers/BeatSyncedContainer.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 6d5f4d63ac..929a300831 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics.Containers /// This container does not set its own clock to the source used for beat matching. /// This means that if the beat source clock is playing faster or slower, animations may unexpectedly overlap. /// Make sure this container's Clock is also set to the expected source (or within a parent element which provides this). + /// + /// This container will also trigger beat events when the beat matching clock is paused at 's BPM. /// public class BeatSyncedContainer : Container { @@ -53,6 +55,9 @@ namespace osu.Game.Graphics.Containers /// public double MinimumBeatLength { get; set; } + /// + /// Whether this container is currently tracking a beatmap's timing data. + /// protected bool IsBeatSyncedWithTrack { get; private set; } protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) @@ -84,35 +89,36 @@ namespace osu.Game.Graphics.Containers ITrack track = null; IBeatmap beatmap = null; - double currentTrackTime = 0; + TimingControlPoint timingPoint; + EffectControlPoint effectPoint; - TimingControlPoint timingPoint = null; - EffectControlPoint effectPoint = null; - - var clock = BeatSyncClock; + IClock clock = BeatSyncClock; if (clock == null) return; + double currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; + if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) { track = Beatmap.Value.Track; beatmap = Beatmap.Value.Beatmap; } - if (track != null && beatmap != null && clock.IsRunning && track.Length > 0) + IsBeatSyncedWithTrack = beatmap != null && clock.IsRunning && track?.Length > 0; + + if (IsBeatSyncedWithTrack) { - currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; + Debug.Assert(beatmap != null); timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); } - - IsBeatSyncedWithTrack = timingPoint?.BeatLength > 0; - - if (!IsBeatSyncedWithTrack) + else { - currentTrackTime = clock.CurrentTime; + // this may be the case where the beat syncing clock has been paused. + // we still want to show an idle animation, so use this container's time instead. + currentTrackTime = Clock.CurrentTime; timingPoint = TimingControlPoint.DEFAULT; effectPoint = EffectControlPoint.DEFAULT; } From 77bfe700e091fd16af9b27edddfd987ec0094579 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:59:57 +0900 Subject: [PATCH 114/489] Add test coverage of idle beat --- .../TestSceneBeatSyncedContainer.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index ac1f88ad33..3ca6bf782a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -78,10 +78,33 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("wait for trigger", () => lastBpm != null); - AddAssert("bpm is from beatmap", () => lastBpm != null&&Precision.AlmostEquals(lastBpm.Value, 128)); + AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128)); AddAssert("beat index is less than zero", () => lastBeatIndex < 0); } + [Test] + public void TestIdleBeatOnPausedClock() + { + double? lastBpm = null; + + AddStep("bind event", () => + { + beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => lastBpm = timingControlPoint.BPM; + }); + + AddUntilStep("wait for trigger", () => lastBpm != null); + AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128)); + + AddStep("pause gameplay clock", () => + { + lastBpm = null; + gameplayClockContainer.Stop(); + }); + + AddUntilStep("wait for trigger", () => lastBpm != null); + AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); + } + private class BeatContainer : BeatSyncedContainer { private const int flash_layer_height = 150; From 98a1f40a982fe52d6482877c2850ab8cc13ab309 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 15:10:14 +0900 Subject: [PATCH 115/489] Ensure `EarlyActivationMilliseconds` is applied even in idle state --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 929a300831..55f694f17e 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -97,7 +97,7 @@ namespace osu.Game.Graphics.Containers if (clock == null) return; - double currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; + double currentTrackTime = clock.CurrentTime; if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) { @@ -123,7 +123,7 @@ namespace osu.Game.Graphics.Containers effectPoint = EffectControlPoint.DEFAULT; } - Debug.Assert(timingPoint != null); + currentTrackTime += EarlyActivationMilliseconds; double beatLength = timingPoint.BeatLength / Divisor; From d0fc25888683d7fc75e4da27c111acc77ad08364 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 15:11:42 +0900 Subject: [PATCH 116/489] Remove unused `OsuLogo.BeatMatching` --- osu.Game/Screens/Menu/OsuLogo.cs | 4 ---- osu.Game/Screens/OsuScreen.cs | 1 - 2 files changed, 5 deletions(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 283be913b0..a9376325cd 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -72,8 +72,6 @@ namespace osu.Game.Screens.Menu set => colourAndTriangles.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint); } - public bool BeatMatching = true; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => logoContainer.ReceivePositionalInputAt(screenSpacePos); public bool Ripple @@ -272,8 +270,6 @@ namespace osu.Game.Screens.Menu { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - if (!BeatMatching) return; - lastBeatIndex = beatIndex; var beatLength = timingPoint.BeatLength; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index aeb51813e4..c3b2612e79 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -242,7 +242,6 @@ namespace osu.Game.Screens logo.Anchor = Anchor.TopLeft; logo.Origin = Anchor.Centre; logo.RelativePositionAxes = Axes.Both; - logo.BeatMatching = true; logo.Triangles = true; logo.Ripple = true; } From 3197f599bb2a9d5a1f516416293d5ac0ab0e636b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:01:46 +0900 Subject: [PATCH 117/489] Add failing test showing `OnNewBeat` can execute far away from an actual beat --- .../TestSceneBeatSyncedContainer.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 3ca6bf782a..5bfaf35bf1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -56,7 +56,34 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestFirstBeatAtFirstTimingPoint() + public void TestSeekBackDoesntPlayMidBeat() + { + int? lastBeatIndex = null; + double? lastActuationTime = null; + TimingControlPoint lastTimingPoint = null; + + AddStep("bind event", () => + { + beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => + { + lastActuationTime = gameplayClockContainer.CurrentTime; + lastTimingPoint = timingControlPoint; + lastBeatIndex = i; + }; + }); + + AddStep("Set time before zero", () => + { + lastBeatIndex = null; + gameplayClockContainer.Seek(-1000); + }); + + AddUntilStep("wait for trigger", () => lastBeatIndex != null); + AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, 32)); + } + + [Test] + public void TestNegativeBeatsStillUsingBeatmapTiming() { int? lastBeatIndex = null; double? lastBpm = null; From b6996d647e9cf62232412e350eb4cc3d98f51680 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:13:13 +0900 Subject: [PATCH 118/489] Add ability to disable mistimed event firings --- .../TestSceneBeatSyncedContainer.cs | 31 ++++++++++++++----- .../Containers/BeatSyncedContainer.cs | 23 ++++++++++++-- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 5bfaf35bf1..2c5433e4aa 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneBeatSyncedContainer : OsuTestScene { - private BeatContainer beatContainer; + private TestBeatSyncedContainer beatContainer; private MasterGameplayClockContainer gameplayClockContainer; @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.UserInterface { gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, 0) { - Child = beatContainer = new BeatContainer + Child = beatContainer = new TestBeatSyncedContainer { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, @@ -55,13 +55,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Start playback", () => gameplayClockContainer.Start()); } - [Test] - public void TestSeekBackDoesntPlayMidBeat() + [TestCase(false)] + [TestCase(true)] + public void TestDisallowMistimedEventFiring(bool allowMistimed) { int? lastBeatIndex = null; double? lastActuationTime = null; TimingControlPoint lastTimingPoint = null; + AddStep("set mistimed to disallow", () => beatContainer.AllowMistimedEventFiring = allowMistimed); + AddStep("bind event", () => { beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => @@ -79,7 +82,15 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("wait for trigger", () => lastBeatIndex != null); - AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, 32)); + + if (!allowMistimed) + { + AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE)); + } + else + { + AddAssert("trigger is not near beat length", () => lastActuationTime != null && lastBeatIndex != null && !Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE)); + } } [Test] @@ -132,10 +143,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); } - private class BeatContainer : BeatSyncedContainer + private class TestBeatSyncedContainer : BeatSyncedContainer { private const int flash_layer_height = 150; + public new bool AllowMistimedEventFiring + { + get => base.AllowMistimedEventFiring; + set => base.AllowMistimedEventFiring = value; + } + private readonly InfoString timingPointCount; private readonly InfoString currentTimingPoint; private readonly InfoString beatCount; @@ -148,7 +165,7 @@ namespace osu.Game.Tests.Visual.UserInterface private readonly Box flashLayer; - public BeatContainer() + public TestBeatSyncedContainer() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 55f694f17e..78ba716cca 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.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 System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -35,6 +36,19 @@ namespace osu.Game.Graphics.Containers /// protected double EarlyActivationMilliseconds; + /// + /// While this container automatically applied an animation delay (meaning any animations inside a implementation will + /// always be correctly timed), the event itself can potentially fire away from the related beat. + /// + /// By setting this to false, cases where the event is to be fired more than from the related beat will be skipped. + /// + protected bool AllowMistimedEventFiring = true; + + /// + /// The maximum deviance from the actual beat that an can fire when is set to false. + /// + public const double MISTIMED_ALLOWANCE = 16; + /// /// The time in milliseconds until the next beat. /// @@ -145,8 +159,13 @@ namespace osu.Game.Graphics.Containers if (timingPoint == lastTimingPoint && beatIndex == lastBeat) return; - using (BeginDelayedSequence(-TimeSinceLastBeat)) - OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty); + // as this event is sometimes used for sound triggers where `BeginDelayedSequence` has no effect, avoid firing it if too far away from the beat. + // this can happen after a seek operation. + if (AllowMistimedEventFiring || Math.Abs(TimeSinceLastBeat) < MISTIMED_ALLOWANCE) + { + using (BeginDelayedSequence(-TimeSinceLastBeat)) + OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty); + } lastBeat = beatIndex; lastTimingPoint = timingPoint; From c38590f1ff11469e024fa94b56837b8a3b92a0bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:42:07 +0900 Subject: [PATCH 119/489] Use a slightly more appropriate metronome sound --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 8f9ff81808..3e08cfcd7a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -353,23 +353,24 @@ namespace osu.Game.Rulesets.Osu.Mods Divisor = 1; } + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) + }; + } + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); if (!IsBeatSyncedWithTrack) return; + sample.Frequency.Value = beatIndex % (int)timingPoint.TimeSignature == 0 ? 1 : 0.5f; sample?.Play(); } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample - }; - } } #endregion From ea87869753a7b99a94f1b9fbb1e3a42a5bacc4a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:50:55 +0900 Subject: [PATCH 120/489] Fix metronome playing during intro time --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3e08cfcd7a..5fa3bc4520 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -341,15 +341,18 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new TargetBeatContainer()); + drawableRuleset.Overlays.Add(new TargetBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } public class TargetBeatContainer : BeatSyncedContainer { + private readonly double firstHitTime; + private PausableSkinnableSound sample; - public TargetBeatContainer() + public TargetBeatContainer(double firstHitTime) { + this.firstHitTime = firstHitTime; Divisor = 1; } @@ -368,8 +371,15 @@ namespace osu.Game.Rulesets.Osu.Mods if (!IsBeatSyncedWithTrack) return; - sample.Frequency.Value = beatIndex % (int)timingPoint.TimeSignature == 0 ? 1 : 0.5f; - sample?.Play(); + int timeSignature = (int)timingPoint.TimeSignature; + + // play metronome from one measure before the first object. + // TODO: Use BeatSyncClock from https://github.com/ppy/osu/pull/13894. + if (Clock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + return; + + sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; + sample.Play(); } } From efdc8fa8a6cfdebabf5b97ab663e910c9eb3d0f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 19:08:12 +0900 Subject: [PATCH 121/489] Fix incorrect step name Co-authored-by: Henry Lin --- .../Visual/UserInterface/TestSceneBeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 2c5433e4aa..6bd5c7bcd8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface double? lastActuationTime = null; TimingControlPoint lastTimingPoint = null; - AddStep("set mistimed to disallow", () => beatContainer.AllowMistimedEventFiring = allowMistimed); + AddStep($"set mistimed to {(allowMistimed ? "allowed" : "disallowed")}", () => beatContainer.AllowMistimedEventFiring = allowMistimed); AddStep("bind event", () => { From fa8e5013c5b51487230d4fd11d7b96f73712bb3e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Jul 2021 18:22:34 +0900 Subject: [PATCH 122/489] Adjust mania speed range --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index e497646a13..13b107457e 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -33,12 +33,12 @@ namespace osu.Game.Rulesets.Mania.UI /// /// The minimum time range. This occurs at a of 40. /// - public const double MIN_TIME_RANGE = 340; + public const double MIN_TIME_RANGE = 290; /// /// The maximum time range. This occurs at a of 1. /// - public const double MAX_TIME_RANGE = 13720; + public const double MAX_TIME_RANGE = 11485; protected new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield; From 8541e73fc1675742c7e720f988a93706662e9315 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 16 Jul 2021 16:26:15 +0700 Subject: [PATCH 123/489] use text flow in markdown image caption --- .../Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs index 1a4f6087c7..501e00bc00 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Wiki.Markdown public class WikiMarkdownImageBlock : FillFlowContainer { [Resolved] - private IMarkdownTextComponent parentTextComponent { get; set; } + private IMarkdownTextFlowComponent parentFlowComponent { get; set; } private readonly LinkInline linkInline; @@ -31,16 +31,20 @@ namespace osu.Game.Overlays.Wiki.Markdown [BackgroundDependencyLoader] private void load() { + MarkdownTextFlowContainer textFlow; + Children = new Drawable[] { new BlockMarkdownImage(linkInline), - parentTextComponent.CreateSpriteText().With(t => + textFlow = parentFlowComponent.CreateTextFlow().With(t => { - t.Text = linkInline.Title; t.Anchor = Anchor.TopCentre; t.Origin = Anchor.TopCentre; + t.TextAnchor = Anchor.TopCentre; }), }; + + textFlow.AddText(linkInline.Title); } private class BlockMarkdownImage : WikiMarkdownImage From 3ac58d6838c573bc9de9fe739e471f99e08bce59 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Jul 2021 19:32:31 +0900 Subject: [PATCH 124/489] Fix min/max values not being passed to inner time range --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 13b107457e..614a7b00c7 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override ScrollVisualisationMethod VisualisationMethod => scrollMethod; private readonly Bindable configDirection = new Bindable(); - private readonly Bindable configTimeRange = new BindableDouble(); + private readonly BindableDouble configTimeRange = new BindableDouble(); // Stores the current speed adjustment active in gameplay. private readonly Track speedAdjustmentTrack = new TrackVirtual(0); @@ -103,6 +103,8 @@ namespace osu.Game.Rulesets.Mania.UI configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange); + TimeRange.MinValue = configTimeRange.MinValue; + TimeRange.MaxValue = configTimeRange.MaxValue; } protected override void AdjustScrollSpeed(int amount) From 05234d6c30c76b2a768bb8b63ded2c958a44ed3c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Jul 2021 19:32:39 +0900 Subject: [PATCH 125/489] Fix mania hitobjects not appearing early enough --- .../Objects/Drawables/DrawableManiaHitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 3ec68bfb56..5aff4e200b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -22,6 +22,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected readonly IBindable Direction = new Bindable(); + // Leaving the default (10s) makes hitobjects not appear, as this offset is used for the initial state transforms. + // Calculated as DrawableManiaRuleset.MAX_TIME_RANGE + some additional allowance for velocity < 1. + protected override double InitialLifetimeOffset => 30000; + [Resolved(canBeNull: true)] private ManiaPlayfield playfield { get; set; } From b3d89254a96774ae0095aff468950d6141458247 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:03:53 +0900 Subject: [PATCH 126/489] Bump `LocalisationAnalyser` for new key generation --- 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 b2f1d6507f..152ba55e08 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 7df7137c8867cd2761dbd59b08b166eba9bed8c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:03:44 +0900 Subject: [PATCH 127/489] Add localisation support for remaining input settings sections --- .../Localisation/BindingSettingsStrings.cs | 29 +++++++++ osu.Game/Localisation/CommonStrings.cs | 17 +++++- .../Localisation/TabletSettingsStrings.cs | 59 +++++++++++++++++++ .../Sections/Input/BindingSettings.cs | 7 ++- .../Sections/Input/RotationPresetButtons.cs | 2 +- .../Settings/Sections/Input/TabletSettings.cs | 25 ++++---- 6 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Localisation/BindingSettingsStrings.cs create mode 100644 osu.Game/Localisation/TabletSettingsStrings.cs diff --git a/osu.Game/Localisation/BindingSettingsStrings.cs b/osu.Game/Localisation/BindingSettingsStrings.cs new file mode 100644 index 0000000000..ad4a650a1f --- /dev/null +++ b/osu.Game/Localisation/BindingSettingsStrings.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class BindingSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.BindingSettings"; + + /// + /// "Shortcut and gameplay bindings" + /// + public static LocalisableString ShortcutAndGameplayBindings => new TranslatableString(getKey(@"shortcut_and_gameplay_bindings"), @"Shortcut and gameplay bindings"); + + /// + /// "Configure" + /// + public static LocalisableString Configure => new TranslatableString(getKey(@"configure"), @"Configure"); + + /// + /// "change global shortcut keys and gameplay bindings" + /// + public static LocalisableString ChangeBindingsButton => new TranslatableString(getKey(@"change_bindings_button"), @"change global shortcut keys and gameplay bindings"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index ad87986554..bf488d2590 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -14,6 +14,21 @@ namespace osu.Game.Localisation /// public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel"); + /// + /// "Enabled" + /// + public static LocalisableString Enabled => new TranslatableString(getKey(@"enabled"), @"Enabled"); + + /// + /// "Width" + /// + public static LocalisableString Width => new TranslatableString(getKey(@"width"), @"Width"); + + /// + /// "Height" + /// + public static LocalisableString Height => new TranslatableString(getKey(@"height"), @"Height"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} +} \ No newline at end of file diff --git a/osu.Game/Localisation/TabletSettingsStrings.cs b/osu.Game/Localisation/TabletSettingsStrings.cs new file mode 100644 index 0000000000..5bdca09e4a --- /dev/null +++ b/osu.Game/Localisation/TabletSettingsStrings.cs @@ -0,0 +1,59 @@ +// 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 TabletSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.TabletSettings"; + + /// + /// "Tablet" + /// + public static LocalisableString Tablet => new TranslatableString(getKey(@"tablet"), @"Tablet"); + + /// + /// "No tablet detected!" + /// + public static LocalisableString NoTabletDetected => new TranslatableString(getKey(@"no_tablet_detected"), @"No tablet detected!"); + + /// + /// "Reset to full area" + /// + public static LocalisableString ResetToFullArea => new TranslatableString(getKey(@"reset_to_full_area"), @"Reset to full area"); + + /// + /// "Conform to current game aspect ratio" + /// + public static LocalisableString ConformToCurrentGameAspectRatio => new TranslatableString(getKey(@"conform_to_current_game_aspect_ratio"), @"Conform to current game aspect ratio"); + + /// + /// "X Offset" + /// + public static LocalisableString XOffset => new TranslatableString(getKey(@"x_offset"), @"X Offset"); + + /// + /// "Y Offset" + /// + public static LocalisableString YOffset => new TranslatableString(getKey(@"y_offset"), @"Y Offset"); + + /// + /// "Rotation" + /// + public static LocalisableString Rotation => new TranslatableString(getKey(@"rotation"), @"Rotation"); + + /// + /// "Aspect Ratio" + /// + public static LocalisableString AspectRatio => new TranslatableString(getKey(@"aspect_ratio"), @"Aspect Ratio"); + + /// + /// "Lock aspect ratio" + /// + public static LocalisableString LockAspectRatio => new TranslatableString(getKey(@"lock_aspect_ratio"), @"Lock aspect ratio"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs index e68cd606e5..3227decc46 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs @@ -3,12 +3,13 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { public class BindingSettings : SettingsSubsection { - protected override LocalisableString Header => "Shortcut and gameplay bindings"; + protected override LocalisableString Header => BindingSettingsStrings.ShortcutAndGameplayBindings; public BindingSettings(KeyBindingPanel keyConfig) { @@ -16,8 +17,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsButton { - Text = "Configure", - TooltipText = "change global shortcut keys and gameplay bindings", + Text = BindingSettingsStrings.Configure, + TooltipText = BindingSettingsStrings.ChangeBindingsButton, Action = keyConfig.ToggleVisibility }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs index 3e8da9f7d0..26610628d5 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input RelativeSizeAxes = Axes.X, Height = height, Width = 0.25f, - Text = $"{presetRotation}º", + Text = $@"{presetRotation}º", Action = () => tabletHandler.Rotation.Value = presetRotation, }); } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index e9ed3378a2..c7342c251d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; using osuTK; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -53,7 +54,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private OsuSpriteText noTabletMessage; - protected override LocalisableString Header => "Tablet"; + protected override LocalisableString Header => TabletSettingsStrings.Tablet; public TabletSettings(ITabletHandler tabletHandler) { @@ -67,14 +68,14 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsCheckbox { - LabelText = "Enabled", + LabelText = CommonStrings.Enabled, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Current = tabletHandler.Enabled }, noTabletMessage = new OsuSpriteText { - Text = "No tablet detected!", + Text = TabletSettingsStrings.NoTabletDetected, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS } @@ -95,7 +96,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, new DangerousSettingsButton { - Text = "Reset to full area", + Text = TabletSettingsStrings.ResetToFullArea, Action = () => { aspectLock.Value = false; @@ -106,7 +107,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, new SettingsButton { - Text = "Conform to current game aspect ratio", + Text = TabletSettingsStrings.ConformToCurrentGameAspectRatio, Action = () => { forceAspectRatio((float)host.Window.ClientSize.Width / host.Window.ClientSize.Height); @@ -115,43 +116,43 @@ namespace osu.Game.Overlays.Settings.Sections.Input new SettingsSlider { TransferValueOnCommit = true, - LabelText = "X Offset", + LabelText = TabletSettingsStrings.XOffset, Current = offsetX }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Y Offset", + LabelText = TabletSettingsStrings.YOffset, Current = offsetY }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Rotation", + LabelText = TabletSettingsStrings.Rotation, Current = rotation }, new RotationPresetButtons(tabletHandler), new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Aspect Ratio", + LabelText = TabletSettingsStrings.AspectRatio, Current = aspectRatio }, new SettingsCheckbox { - LabelText = "Lock aspect ratio", + LabelText = TabletSettingsStrings.LockAspectRatio, Current = aspectLock }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Width", + LabelText = CommonStrings.Width, Current = sizeX }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Height", + LabelText = CommonStrings.Height, Current = sizeY }, } From 3e8a13bfbfba8ab70203435e0ed51c4e6aca6907 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Fri, 16 Jul 2021 16:16:34 +0200 Subject: [PATCH 128/489] Allow interacting with timeline objects outside of drawable bounds --- .../Timeline/TimelineBlueprintContainer.cs | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index a642768574..cb733ab17d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -31,18 +31,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved(CanBeNull = true)] private Timeline timeline { get; set; } - [Resolved] - private OsuColour colours { get; set; } - private DragEvent lastDragEvent; private Bindable placement; private SelectionBlueprint placementBlueprint; - private SelectableAreaBackground backgroundBox; + // We want children to be able to be clicked and dragged + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - // we only care about checking vertical validity. - // this allows selecting and dragging selections before time=0. - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + // This drawable itself should still check whether the mouse is over it + private bool shouldHandleInputAt(Vector2 screenSpacePos) { float localY = ToLocalSpace(screenSpacePos).Y; return DrawRectangle.Top <= localY && DrawRectangle.Bottom >= localY; @@ -61,7 +58,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load() { - AddInternal(backgroundBox = new SelectableAreaBackground + AddInternal(new SelectableAreaBackground { Colour = Color4.Black, Depth = float.MaxValue, @@ -100,16 +97,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected override bool OnHover(HoverEvent e) + protected override bool OnDragStart(DragStartEvent e) { - backgroundBox.FadeColour(colours.BlueLighter, 120, Easing.OutQuint); - return base.OnHover(e); - } + if (!shouldHandleInputAt(e.ScreenSpaceMouseDownPosition)) + return false; - protected override void OnHoverLost(HoverLostEvent e) - { - backgroundBox.FadeColour(Color4.Black, 600, Easing.OutQuint); - base.OnHoverLost(e); + return base.OnDragStart(e); } protected override void OnDrag(DragEvent e) @@ -184,7 +177,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { return new TimelineHitObjectBlueprint(item) { - OnDragHandled = handleScrollViaDrag + OnDragHandled = handleScrollViaDrag, }; } @@ -212,6 +205,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private class SelectableAreaBackground : CompositeDrawable { + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + { + float localY = ToLocalSpace(screenSpacePos).Y; + return DrawRectangle.Top <= localY && DrawRectangle.Bottom >= localY; + } + [BackgroundDependencyLoader] private void load() { @@ -235,6 +234,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } }); } + + [Resolved] + private OsuColour colours { get; set; } + + protected override bool OnHover(HoverEvent e) + { + this.FadeColour(colours.BlueLighter, 120, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + this.FadeColour(Color4.Black, 600, Easing.OutQuint); + base.OnHoverLost(e); + } } internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler From bda16f0fbc7c818ad22821e31625c8150b80877b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:51:31 +0900 Subject: [PATCH 129/489] Fix progression lines getting stuck after removing a match in ladder editor screen --- .../Screens/Ladder/Components/DrawableTournamentMatch.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs index 1c805bb42e..6937c69dbf 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs @@ -303,6 +303,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Match.LosersProgression.Value = null; ladderInfo.Matches.Remove(Match); + + foreach (var m in ladderInfo.Matches) + { + if (m.Progression.Value == Match) + m.Progression.Value = null; + + if (m.LosersProgression.Value == Match) + m.LosersProgression.Value = null; + } } } } From e35cff99c79490faa9d7789aa84031763c6dc4e3 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Fri, 16 Jul 2021 17:21:43 +0200 Subject: [PATCH 130/489] Pass on mouseDown input to timeline if no selection modification is made with that input --- .../Timeline/TimelineBlueprintContainer.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index cb733ab17d..abd7f5923c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -97,6 +97,19 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected override bool OnMouseDown(MouseDownEvent e) + { + int selectionCount = SelectedItems.Count; + + // We let BlueprintContainer attempt a HitObject selection + // If it fails, we'll pass it this input back to the timeline so it can be dragged + // We know it failed if the selection count is unchanged after the selection attempt + if (base.OnMouseDown(e) && selectionCount != SelectedItems.Count) + return true; + + return false; + } + protected override bool OnDragStart(DragStartEvent e) { if (!shouldHandleInputAt(e.ScreenSpaceMouseDownPosition)) From 83ebbb7f8e990fe78f2311ad9d75970959979f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 00:21:58 +0900 Subject: [PATCH 131/489] Allow the schedule screen to show even when a current match is not selected --- .../Screens/TestSceneScheduleScreen.cs | 6 + .../Screens/Schedule/ScheduleScreen.cs | 106 +++++++++--------- 2 files changed, 60 insertions(+), 52 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs index 0da8d1eb4a..bd1bacd549 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs @@ -28,6 +28,12 @@ namespace osu.Game.Tournament.Tests.Screens setMatchDate(TimeSpan.FromHours(3)); } + [Test] + public void TestNoCurrentMatch() + { + AddStep("Set null current match", () => Ladder.CurrentMatch.Value = null); + } + private void setMatchDate(TimeSpan relativeTime) // Humanizer cannot handle negative timespans. => AddStep($"start time is {relativeTime}", () => diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index c1d8c8ddd3..e08be65465 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -96,19 +96,18 @@ namespace osu.Game.Tournament.Screens.Schedule } }, }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); - currentMatch.BindValueChanged(matchChanged); currentMatch.BindTo(ladder.CurrentMatch); + currentMatch.BindValueChanged(matchChanged, true); } private void matchChanged(ValueChangedEvent match) { - if (match.NewValue == null) - { - mainContainer.Clear(); - return; - } - var upcoming = ladder.Matches.Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4); var conditionals = ladder .Matches.Where(p => !p.Completed.Value && (p.Team1.Value == null || p.Team2.Value == null) && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) @@ -117,6 +116,8 @@ namespace osu.Game.Tournament.Screens.Schedule upcoming = upcoming.Concat(conditionals); upcoming = upcoming.OrderBy(p => p.Date.Value).Take(8); + ScheduleContainer comingUpNext; + mainContainer.Child = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -153,57 +154,58 @@ namespace osu.Game.Tournament.Screens.Schedule } } }, - new ScheduleContainer("coming up next") + comingUpNext = new ScheduleContainer("coming up next") { RelativeSizeAxes = Axes.Both, Height = 0.25f, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(30), - Children = new Drawable[] - { - new ScheduleMatch(match.NewValue, false) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - new TournamentSpriteTextWithBackground(match.NewValue.Round.Value?.Name.Value) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.5f) - }, - new TournamentSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = match.NewValue.Team1.Value?.FullName + " vs " + match.NewValue.Team2.Value?.FullName, - Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold) - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Children = new Drawable[] - { - new ScheduleMatchDate(match.NewValue.Date.Value) - { - Font = OsuFont.Torus.With(size: 24, weight: FontWeight.Regular) - } - } - }, - } - }, - } } } }; + + if (match.NewValue != null) + { + comingUpNext.Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(30), + Children = new Drawable[] + { + new ScheduleMatch(match.NewValue, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new TournamentSpriteTextWithBackground(match.NewValue.Round.Value?.Name.Value) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.5f) + }, + new TournamentSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = match.NewValue.Team1.Value?.FullName + " vs " + match.NewValue.Team2.Value?.FullName, + Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold) + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + new ScheduleMatchDate(match.NewValue.Date.Value) + { + Font = OsuFont.Torus.With(size: 24, weight: FontWeight.Regular) + } + } + }, + } + }; + } } public class ScheduleMatch : DrawableTournamentMatch From 71f74f0e9860bcca7054df6bf40ec67cabdd3e5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:34:28 +0900 Subject: [PATCH 132/489] Add warning message to screens which require a current match to be selected --- .../Screens/MapPool/MapPoolScreen.cs | 40 +++++++++---------- .../Screens/TournamentMatchScreen.cs | 34 ++++++++++++++++ 2 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 osu.Game.Tournament/Screens/TournamentMatchScreen.cs diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 2c4fed8d86..d4292c5492 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -21,12 +21,10 @@ using osuTK.Input; namespace osu.Game.Tournament.Screens.MapPool { - public class MapPoolScreen : TournamentScreen + public class MapPoolScreen : TournamentMatchScreen { private readonly FillFlowContainer> mapFlows; - private readonly Bindable currentMatch = new Bindable(); - [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -96,7 +94,7 @@ namespace osu.Game.Tournament.Screens.MapPool Action = reset }, new ControlPanel.Spacer(), - } + }, } }; } @@ -104,15 +102,12 @@ namespace osu.Game.Tournament.Screens.MapPool [BackgroundDependencyLoader] private void load(MatchIPCInfo ipc) { - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(LadderInfo.CurrentMatch); - ipc.Beatmap.BindValueChanged(beatmapChanged); } private void beatmapChanged(ValueChangedEvent beatmap) { - if (currentMatch.Value == null || currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) + if (CurrentMatch.Value == null || CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) return; // if bans have already been placed, beatmap changes result in a selection being made autoamtically @@ -137,12 +132,12 @@ namespace osu.Game.Tournament.Screens.MapPool { const TeamColour roll_winner = TeamColour.Red; //todo: draw from match - var nextColour = (currentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + var nextColour = (CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; - if (pickType == ChoiceType.Ban && currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2) + if (pickType == ChoiceType.Ban && CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2) setMode(pickColour, ChoiceType.Pick); else - setMode(nextColour, currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); + setMode(nextColour, CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); } protected override bool OnMouseDown(MouseDownEvent e) @@ -156,11 +151,11 @@ namespace osu.Game.Tournament.Screens.MapPool addForBeatmap(map.Beatmap.OnlineBeatmapID.Value); else { - var existing = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID); + var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID); if (existing != null) { - currentMatch.Value.PicksBans.Remove(existing); + CurrentMatch.Value.PicksBans.Remove(existing); setNextMode(); } } @@ -173,7 +168,7 @@ namespace osu.Game.Tournament.Screens.MapPool private void reset() { - currentMatch.Value.PicksBans.Clear(); + CurrentMatch.Value.PicksBans.Clear(); setNextMode(); } @@ -181,18 +176,18 @@ namespace osu.Game.Tournament.Screens.MapPool private void addForBeatmap(int beatmapId) { - if (currentMatch.Value == null) + if (CurrentMatch.Value == null) return; - if (currentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) + if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) // don't attempt to add if the beatmap isn't in our pool return; - if (currentMatch.Value.PicksBans.Any(p => p.BeatmapID == beatmapId)) + if (CurrentMatch.Value.PicksBans.Any(p => p.BeatmapID == beatmapId)) // don't attempt to add if already exists. return; - currentMatch.Value.PicksBans.Add(new BeatmapChoice + CurrentMatch.Value.PicksBans.Add(new BeatmapChoice { Team = pickColour, Type = pickType, @@ -201,17 +196,22 @@ namespace osu.Game.Tournament.Screens.MapPool setNextMode(); - if (pickType == ChoiceType.Pick && currentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick)) + if (pickType == ChoiceType.Pick && CurrentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick)) { scheduledChange?.Cancel(); scheduledChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000); } } - private void matchChanged(ValueChangedEvent match) + protected override void CurrentMatchChanged(ValueChangedEvent match) { + base.CurrentMatchChanged(match); + mapFlows.Clear(); + if (match.NewValue == null) + return; + int totalRows = 0; if (match.NewValue.Round.Value != null) diff --git a/osu.Game.Tournament/Screens/TournamentMatchScreen.cs b/osu.Game.Tournament/Screens/TournamentMatchScreen.cs new file mode 100644 index 0000000000..5f00036653 --- /dev/null +++ b/osu.Game.Tournament/Screens/TournamentMatchScreen.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Tournament.Models; + +namespace osu.Game.Tournament.Screens +{ + public abstract class TournamentMatchScreen : TournamentScreen + { + protected readonly Bindable CurrentMatch = new Bindable(); + private WarningBox noMatchWarning; + + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentMatch.BindTo(LadderInfo.CurrentMatch); + CurrentMatch.BindValueChanged(CurrentMatchChanged, true); + } + + protected virtual void CurrentMatchChanged(ValueChangedEvent match) + { + if (match.NewValue == null) + { + AddInternal(noMatchWarning = new WarningBox("Choose a match first from the brackets screen")); + return; + } + + noMatchWarning?.Expire(); + noMatchWarning = null; + } + } +} From 0a13e033eaa6907126fa852eb7dc71a8e587bc12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:59:16 +0900 Subject: [PATCH 133/489] Move height warning to bottom of screen to avoid overlap --- osu.Game.Tournament/TournamentGame.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 87e23e3404..cd0e601a2f 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -97,7 +97,12 @@ namespace osu.Game.Tournament }, } }, - heightWarning = new WarningBox("Please make the window wider"), + heightWarning = new WarningBox("Please make the window wider") + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Margin = new MarginPadding(20), + }, new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, From e8595871ded0ad53f3f3caf6f79f00a952523a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 00:14:48 +0900 Subject: [PATCH 134/489] Update remaining screens to also show the warning message --- .../Screens/BeatmapInfoScreen.cs | 2 +- .../Screens/Gameplay/GameplayScreen.cs | 29 ++++++++++--------- .../Screens/Showcase/ShowcaseScreen.cs | 8 +++++ .../Screens/TeamIntro/SeedingScreen.cs | 22 +++++++------- .../Screens/TeamIntro/TeamIntroScreen.cs | 16 ++++------ .../Screens/TeamWin/TeamWinScreen.cs | 19 ++++++------ 6 files changed, 52 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 0a3163ef43..50498304ca 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -11,7 +11,7 @@ using osu.Game.Tournament.IPC; namespace osu.Game.Tournament.Screens { - public abstract class BeatmapInfoScreen : TournamentScreen + public abstract class BeatmapInfoScreen : TournamentMatchScreen { protected readonly SongBar SongBar; diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 6e4c6784c8..f61506d7f2 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -24,8 +24,6 @@ namespace osu.Game.Tournament.Screens.Gameplay { private readonly BindableBool warmup = new BindableBool(); - private readonly Bindable currentMatch = new Bindable(); - public readonly Bindable State = new Bindable(); private OsuButton warmupButton; private MatchIPCInfo ipc; @@ -131,14 +129,6 @@ namespace osu.Game.Tournament.Screens.Gameplay ladder.ChromaKeyWidth.BindValueChanged(width => chroma.Width = width.NewValue, true); - currentMatch.BindValueChanged(m => - { - warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0; - scheduledOperation?.Cancel(); - }); - - currentMatch.BindTo(ladder.CurrentMatch); - warmup.BindValueChanged(w => { warmupButton.Alpha = !w.NewValue ? 0.5f : 1; @@ -146,6 +136,17 @@ namespace osu.Game.Tournament.Screens.Gameplay }, true); } + protected override void CurrentMatchChanged(ValueChangedEvent match) + { + base.CurrentMatchChanged(match); + + if (match.NewValue == null) + return; + + warmup.Value = match.NewValue.Team1Score.Value + match.NewValue.Team2Score.Value == 0; + scheduledOperation?.Cancel(); + } + private ScheduledDelegate scheduledOperation; private MatchScoreDisplay scoreDisplay; @@ -161,9 +162,9 @@ namespace osu.Game.Tournament.Screens.Gameplay if (warmup.Value) return; if (ipc.Score1.Value > ipc.Score2.Value) - currentMatch.Value.Team1Score.Value++; + CurrentMatch.Value.Team1Score.Value++; else - currentMatch.Value.Team2Score.Value++; + CurrentMatch.Value.Team2Score.Value++; } scheduledOperation?.Cancel(); @@ -198,9 +199,9 @@ namespace osu.Game.Tournament.Screens.Gameplay // we should automatically proceed after a short delay if (lastState == TourneyState.Ranking && !warmup.Value) { - if (currentMatch.Value?.Completed.Value == true) + if (CurrentMatch.Value?.Completed.Value == true) scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, delay_before_progression); - else if (currentMatch.Value?.Completed.Value == false) + else if (CurrentMatch.Value?.Completed.Value == false) scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, delay_before_progression); } diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 9785b7e647..32d458e191 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Tournament.Components; using osu.Framework.Graphics.Shapes; +using osu.Game.Tournament.Models; using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Showcase @@ -39,5 +41,11 @@ namespace osu.Game.Tournament.Screens.Showcase } }); } + + protected override void CurrentMatchChanged(ValueChangedEvent match) + { + // showcase screen doesn't care about a match being selected. + // base call intentionally omitted to not show match warning. + } } } diff --git a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs index 4f66d89b7f..71aed69738 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs @@ -18,12 +18,10 @@ using osuTK; namespace osu.Game.Tournament.Screens.TeamIntro { - public class SeedingScreen : TournamentScreen, IProvideVideo + public class SeedingScreen : TournamentMatchScreen, IProvideVideo { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); - private readonly Bindable currentTeam = new Bindable(); [BackgroundDependencyLoader] @@ -50,13 +48,13 @@ namespace osu.Game.Tournament.Screens.TeamIntro { RelativeSizeAxes = Axes.X, Text = "Show first team", - Action = () => currentTeam.Value = currentMatch.Value.Team1.Value, + Action = () => currentTeam.Value = CurrentMatch.Value.Team1.Value, }, new TourneyButton { RelativeSizeAxes = Axes.X, Text = "Show second team", - Action = () => currentTeam.Value = currentMatch.Value.Team2.Value, + Action = () => currentTeam.Value = CurrentMatch.Value.Team2.Value, }, new SettingsTeamDropdown(LadderInfo.Teams) { @@ -67,9 +65,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro } }; - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(LadderInfo.CurrentMatch); - currentTeam.BindValueChanged(teamChanged, true); } @@ -84,8 +79,15 @@ namespace osu.Game.Tournament.Screens.TeamIntro showTeam(team.NewValue); } - private void matchChanged(ValueChangedEvent match) => - currentTeam.Value = currentMatch.Value.Team1.Value; + protected override void CurrentMatchChanged(ValueChangedEvent match) + { + base.CurrentMatchChanged(match); + + if (match.NewValue == null) + return; + + currentTeam.Value = match.NewValue.Team1.Value; + } private void showTeam(TournamentTeam team) { diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 6c2848897b..74957cbca5 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -12,12 +12,10 @@ using osuTK; namespace osu.Game.Tournament.Screens.TeamIntro { - public class TeamIntroScreen : TournamentScreen, IProvideVideo + public class TeamIntroScreen : TournamentMatchScreen, IProvideVideo { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); - [BackgroundDependencyLoader] private void load(Storage storage) { @@ -35,18 +33,16 @@ namespace osu.Game.Tournament.Screens.TeamIntro RelativeSizeAxes = Axes.Both, } }; - - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(LadderInfo.CurrentMatch); } - private void matchChanged(ValueChangedEvent match) + protected override void CurrentMatchChanged(ValueChangedEvent match) { + base.CurrentMatchChanged(match); + + mainContainer.Clear(); + if (match.NewValue == null) - { - mainContainer.Clear(); return; - } const float y_flag_offset = 292; diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 7ca262a2e8..ebe2908b74 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -13,11 +13,10 @@ using osuTK; namespace osu.Game.Tournament.Screens.TeamWin { - public class TeamWinScreen : TournamentScreen, IProvideVideo + public class TeamWinScreen : TournamentMatchScreen, IProvideVideo { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); private readonly Bindable currentCompleted = new Bindable(); private TourneyVideo blueWinVideo; @@ -48,17 +47,19 @@ namespace osu.Game.Tournament.Screens.TeamWin } }; - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(ladder.CurrentMatch); - currentCompleted.BindValueChanged(_ => update()); } - private void matchChanged(ValueChangedEvent match) + protected override void CurrentMatchChanged(ValueChangedEvent match) { - currentCompleted.UnbindBindings(); - currentCompleted.BindTo(match.NewValue.Completed); + base.CurrentMatchChanged(match); + currentCompleted.UnbindBindings(); + + if (match.NewValue == null) + return; + + currentCompleted.BindTo(match.NewValue.Completed); update(); } @@ -66,7 +67,7 @@ namespace osu.Game.Tournament.Screens.TeamWin private void update() => Schedule(() => { - var match = currentMatch.Value; + var match = CurrentMatch.Value; if (match.Winner == null) { From 7a671754f2c067b38ab8b4515fbd718cbcf8228e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:29:31 +0900 Subject: [PATCH 135/489] Change `RadioButton`'s `object` to a `string` --- .../Edit/Components/RadioButtons/RadioButton.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index dcf5f8a788..c3ffd8add0 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// /// The item related to this button. /// - public object Item; + public string Item; /// /// A function which creates a drawable icon to represent this item. If null, a sane default should be used. @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons private readonly Action action; - public RadioButton(object item, Action action, Func createIcon = null) + public RadioButton(string item, Action action, Func createIcon = null) { Item = item; CreateIcon = createIcon; @@ -34,13 +34,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Selected = new BindableBool(); } - public RadioButton(string item) - : this(item, null) - { - Item = item; - action = null; - } - /// /// Selects this . /// From 3ae5f6707abe50ffa9c4ba9bae2a359a0dc49468 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:30:00 +0900 Subject: [PATCH 136/489] Expose whether an `EditorBeatmap` has timing present or not via bindable --- osu.Game/Screens/Edit/EditorBeatmap.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index be53abbd55..7de98e5e85 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -46,12 +46,22 @@ namespace osu.Game.Screens.Edit public readonly IBeatmap PlayableBeatmap; + /// + /// Whether at least one timing control point is present and providing timing information. + /// + public IBindable HasTiming => hasTiming; + + private readonly Bindable hasTiming = new Bindable(); + [CanBeNull] public readonly ISkin BeatmapSkin; [Resolved] private BindableBeatDivisor beatDivisor { get; set; } + [Resolved] + private EditorClock editorClock { get; set; } + private readonly IBeatmapProcessor beatmapProcessor; private readonly Dictionary> startTimeBindables = new Dictionary>(); @@ -238,6 +248,8 @@ namespace osu.Game.Screens.Edit if (batchPendingUpdates.Count > 0) UpdateState(); + + hasTiming.Value = !ReferenceEquals(ControlPointInfo.TimingPointAt(editorClock.CurrentTime), TimingControlPoint.DEFAULT); } protected override void UpdateState() From eac9b1ec7e32eee5998cf4ff95cc464e04375eb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:30:13 +0900 Subject: [PATCH 137/489] Disable toolbox composition buttons when beatmap is not timed --- .../Editing/TestSceneHitObjectComposer.cs | 105 ++++++++++++++---- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 +- .../RadioButtons/DrawableRadioButton.cs | 27 +++-- 3 files changed, 103 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 7ca24346aa..f75c976f56 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -2,17 +2,24 @@ // 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.Timing; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Components.RadioButtons; +using osu.Game.Screens.Edit.Compose.Components; using osuTK; namespace osu.Game.Tests.Visual.Editing @@ -20,37 +27,89 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneHitObjectComposer : EditorClockTestScene { - [BackgroundDependencyLoader] - private void load() + private OsuHitObjectComposer hitObjectComposer; + private EditorBeatmapContainer editorBeatmapContainer; + + private EditorBeatmap editorBeatmap => editorBeatmapContainer.EditorBeatmap; + + [SetUpSteps] + public void SetUpSteps() { - Beatmap.Value = CreateWorkingBeatmap(new Beatmap + AddStep("create beatmap", () => { - HitObjects = new List + Beatmap.Value = CreateWorkingBeatmap(new Beatmap { - new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, - new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, - new Slider + HitObjects = new List { - Position = new Vector2(128, 256), - Path = new SliderPath(PathType.Linear, new[] + new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, + new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, + new Slider { - Vector2.Zero, - new Vector2(216, 0), - }), - Scale = 0.5f, - } - }, + Position = new Vector2(128, 256), + Path = new SliderPath(PathType.Linear, new[] + { + Vector2.Zero, + new Vector2(216, 0), + }), + Scale = 0.5f, + } + }, + }); }); - var editorBeatmap = new EditorBeatmap(Beatmap.Value.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + AddStep("Create composer", () => + { + Child = editorBeatmapContainer = new EditorBeatmapContainer(Beatmap.Value) + { + Child = hitObjectComposer = new OsuHitObjectComposer(new OsuRuleset()) + }; + }); + } - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - Dependencies.CacheAs(clock); - Dependencies.CacheAs(clock); - Dependencies.CacheAs(editorBeatmap); - Dependencies.CacheAs(editorBeatmap); + [Test] + public void TestPlacementOnlyWorksWithTiming() + { + AddStep("clear all control points", () => editorBeatmap.ControlPointInfo.Clear()); - Child = new OsuHitObjectComposer(new OsuRuleset()); + AddAssert("Tool is selection", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is SelectTool); + AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); + AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); + AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Click()); + AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); + } + + public class EditorBeatmapContainer : Container + { + private readonly WorkingBeatmap working; + + public EditorBeatmap EditorBeatmap { get; private set; } + + public EditorBeatmapContainer(WorkingBeatmap working) + { + this.working = working; + + RelativeSizeAxes = Axes.Both; + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + EditorBeatmap = new EditorBeatmap(working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + + dependencies.CacheAs(EditorBeatmap); + dependencies.CacheAs(EditorBeatmap); + + return dependencies; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(EditorBeatmap); + } } } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index a7005954b2..c6c112bec8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -219,7 +219,8 @@ namespace osu.Game.Rulesets.Edit if (item != null) { - item.Select(); + if (!item.Selected.Disabled) + item.Select(); return true; } } diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs index 1f608d28fd..79468f0ab0 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs @@ -5,9 +5,11 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -16,24 +18,28 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.RadioButtons { - public class DrawableRadioButton : OsuButton + public class DrawableRadioButton : OsuButton, IHasTooltip { /// /// Invoked when this has been selected. /// public Action Selected; + public readonly RadioButton Button; + private Color4 defaultBackgroundColour; private Color4 defaultBubbleColour; private Color4 selectedBackgroundColour; private Color4 selectedBubbleColour; private Drawable icon; - private readonly RadioButton button; + + [Resolved(canBeNull: true)] + private EditorBeatmap editorBeatmap { get; set; } public DrawableRadioButton(RadioButton button) { - this.button = button; + this.Button = button; Text = button.Item.ToString(); Action = button.Select; @@ -57,7 +63,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Colour = Color4.Black.Opacity(0.5f) }; - Add(icon = (button.CreateIcon?.Invoke() ?? new Circle()).With(b => + Add(icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b => { b.Blending = BlendingParameters.Additive; b.Anchor = Anchor.CentreLeft; @@ -71,13 +77,16 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons { base.LoadComplete(); - button.Selected.ValueChanged += selected => + Button.Selected.ValueChanged += selected => { updateSelectionState(); if (selected.NewValue) - Selected?.Invoke(button); + Selected?.Invoke(Button); }; + editorBeatmap?.HasTiming.BindValueChanged(hasTiming => Button.Selected.Disabled = !hasTiming.NewValue, true); + + Button.Selected.BindDisabledChanged(disabled => Enabled.Value = !disabled, true); updateSelectionState(); } @@ -86,8 +95,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons if (!IsLoaded) return; - BackgroundColour = button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour; - icon.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour; + BackgroundColour = Button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour; + icon.Colour = Button.Selected.Value ? selectedBubbleColour : defaultBubbleColour; } protected override SpriteText CreateText() => new OsuSpriteText @@ -97,5 +106,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Anchor = Anchor.CentreLeft, X = 40f }; + + public LocalisableString TooltipText => Enabled.Value ? string.Empty : "Add at least one timing point first!"; } } From 50eed26bd1addd0f929db1b0a55609e7e7cc8b97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:30:49 +0900 Subject: [PATCH 138/489] Rename radio button `item` to `label` --- osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 6 +++--- .../Edit/Components/RadioButtons/DrawableRadioButton.cs | 4 ++-- .../Screens/Edit/Components/RadioButtons/RadioButton.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index f75c976f56..67c37413ed 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -72,10 +72,10 @@ namespace osu.Game.Tests.Visual.Editing AddStep("clear all control points", () => editorBeatmap.ControlPointInfo.Clear()); AddAssert("Tool is selection", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is SelectTool); - AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); + AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); - AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); - AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Click()); + AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Click()); AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); } diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs index 79468f0ab0..bbe77eaa07 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs @@ -39,9 +39,9 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons public DrawableRadioButton(RadioButton button) { - this.Button = button; + Button = button; - Text = button.Item.ToString(); + Text = button.Label; Action = button.Select; RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index c3ffd8add0..ca79dd15d7 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// /// The item related to this button. /// - public string Item; + public string Label; /// /// A function which creates a drawable icon to represent this item. If null, a sane default should be used. @@ -26,9 +26,9 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons private readonly Action action; - public RadioButton(string item, Action action, Func createIcon = null) + public RadioButton(string label, Action action, Func createIcon = null) { - Item = item; + Label = label; CreateIcon = createIcon; this.action = action; Selected = new BindableBool(); From db4d64effb1f208b528af2ff4d0bd5a1954d86f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:29:18 +0900 Subject: [PATCH 139/489] Rename incorrect step --- .../Visual/UserInterface/TestSceneBeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 6bd5c7bcd8..6b56f339d8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("wait for trigger", () => lastBpm != null); - AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); + AddAssert("bpm is default", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); } private class TestBeatSyncedContainer : BeatSyncedContainer From 23ed77f2c6f10135e3b849e5adf96008372b9cd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:33:02 +0900 Subject: [PATCH 140/489] Fix test failure under visual tests due to double firing --- .../UserInterface/TestSceneBeatSyncedContainer.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 6b56f339d8..e5bcc08924 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -65,19 +65,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep($"set mistimed to {(allowMistimed ? "allowed" : "disallowed")}", () => beatContainer.AllowMistimedEventFiring = allowMistimed); - AddStep("bind event", () => + AddStep("Set time before zero", () => { beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => { lastActuationTime = gameplayClockContainer.CurrentTime; lastTimingPoint = timingControlPoint; lastBeatIndex = i; + beatContainer.NewBeat = null; }; - }); - AddStep("Set time before zero", () => - { - lastBeatIndex = null; gameplayClockContainer.Seek(-1000); }); @@ -99,19 +96,14 @@ namespace osu.Game.Tests.Visual.UserInterface int? lastBeatIndex = null; double? lastBpm = null; - AddStep("bind event", () => + AddStep("Set time before zero", () => { beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => { lastBeatIndex = i; lastBpm = timingControlPoint.BPM; }; - }); - AddStep("Set time before zero", () => - { - lastBeatIndex = null; - lastBpm = null; gameplayClockContainer.Seek(-1000); }); From 87d3bd4b9389a3582f15ccfbf054dd8b3fe8e01a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:35:43 +0900 Subject: [PATCH 141/489] Fix time until next beat potentially being exactly zero at point of trigger --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 78ba716cca..6e4901ab1a 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -151,7 +151,7 @@ namespace osu.Game.Graphics.Containers beatIndex--; TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % beatLength; - if (TimeUntilNextBeat < 0) + if (TimeUntilNextBeat <= 0) TimeUntilNextBeat += beatLength; TimeSinceLastBeat = beatLength - TimeUntilNextBeat; From d609839ff68e27e1e76d12d3f3cec601d8d68ce6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:56:10 +0900 Subject: [PATCH 142/489] Fix test not working due to popover container being too global --- .../Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs | 5 ++++- osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 3ba0f4969a..27fc1dcd51 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,6 +3,9 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; @@ -60,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("attempt join room", () => InputManager.Key(Key.Enter)); - AddUntilStep("password prompt appeared", () => (passwordEntryPopover = loungeScreen.ChildrenOfType().FirstOrDefault()) != null); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 01dd7a25c8..5d2309b88c 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; @@ -35,8 +36,9 @@ namespace osu.Game.Tests.Visual MenuCursorContainer cursorContainer; CompositeDrawable mainContent = - (cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }) - .WithChild(content = new OsuTooltipContainer(cursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }); + new PopoverContainer { RelativeSizeAxes = Axes.Both } + .WithChild(cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }) + .WithChild(content = new OsuTooltipContainer(cursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }); if (CreateNestedActionContainer) { From c966cb053024e180838a8538ec6d46d0f9eb09bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 15:04:18 +0900 Subject: [PATCH 143/489] Fix dependency lookup failing due to location of `PopoverContainer` --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 84c20a6acc..951b00376e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -243,7 +243,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Alpha = 0; } - public Popover GetPopover() => new PasswordEntryPopover(Room); + public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; public MenuItem[] ContextMenuItems => new MenuItem[] { @@ -350,8 +350,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { private readonly Room room; - [Resolved(canBeNull: true)] - private LoungeSubScreen lounge { get; set; } + public Action JoinRequested; public PasswordEntryPopover(Room room) { @@ -379,7 +378,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Width = 80, Text = "Join Room", - Action = () => lounge?.Join(room, passwordTextbox.Text) + Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text) } } }; From 567a94a28bd90a5082fda0c43a54a38cc1fdbbda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 15:35:08 +0900 Subject: [PATCH 144/489] Remove unused using statements --- .../Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 27fc1dcd51..bde9ea89ed 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,9 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; From 64cf9b702ededf6e54e4908df54630baec3130c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 17:26:11 +0900 Subject: [PATCH 145/489] Fix incorrec nesting of manual input manager test containers --- .../Tests/Visual/OsuManualInputManagerTestScene.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 5d2309b88c..c5e2e67eaf 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -35,10 +35,16 @@ namespace osu.Game.Tests.Visual { MenuCursorContainer cursorContainer; - CompositeDrawable mainContent = - new PopoverContainer { RelativeSizeAxes = Axes.Both } - .WithChild(cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }) - .WithChild(content = new OsuTooltipContainer(cursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }); + CompositeDrawable mainContent = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both, } + }; + + cursorContainer.Child = content = new OsuTooltipContainer(cursorContainer.Cursor) + { + RelativeSizeAxes = Axes.Both + }; if (CreateNestedActionContainer) { From ba9b51c12d5a4c49b96a9e2db71760290cb546c8 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 19:25:25 +0800 Subject: [PATCH 146/489] Add localisation for WikiHeader Co-authored-by: huoyaoyuan --- osu.Game/Localisation/WikiStrings.cs | 29 ++++++++++++++++++++++++++++ osu.Game/Overlays/Wiki/WikiHeader.cs | 24 +++++++++++++---------- 2 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Localisation/WikiStrings.cs diff --git a/osu.Game/Localisation/WikiStrings.cs b/osu.Game/Localisation/WikiStrings.cs new file mode 100644 index 0000000000..6176fd4e85 --- /dev/null +++ b/osu.Game/Localisation/WikiStrings.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class WikiStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.Wiki"; + + /// + /// "index" + /// + public static LocalisableString IndexPageString => new TranslatableString(getKey(@"index_page"), @"index"); + + /// + /// "wiki" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"wiki"); + + /// + /// "knowledge base" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"knowledge base"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 6b8cba48b4..4bec42b6ce 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -5,14 +5,18 @@ using System; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Wiki { public class WikiHeader : BreadcrumbControlOverlayHeader { - private const string index_page_string = "index"; private const string index_path = "Main_Page"; + public static LocalisableString IndexPageString => WikiStrings.IndexPageString; + public static LocalisableString HeaderTitle => WikiStrings.HeaderTitle; + public static LocalisableString HeaderDescription => WikiStrings.HeaderDescription; public readonly Bindable WikiPageData = new Bindable(); @@ -21,8 +25,8 @@ namespace osu.Game.Overlays.Wiki public WikiHeader() { - TabControl.AddItem(index_page_string); - Current.Value = index_page_string; + TabControl.AddItem(IndexPageString); + Current.Value = IndexPageString; WikiPageData.BindValueChanged(onWikiPageChange); Current.BindValueChanged(onCurrentChange); @@ -34,13 +38,13 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = null; + Current.Value = string.Empty; - TabControl.AddItem(index_page_string); + TabControl.AddItem(IndexPageString); if (e.NewValue.Path == index_path) { - Current.Value = index_page_string; + Current.Value = IndexPageString; return; } @@ -51,12 +55,12 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } - private void onCurrentChange(ValueChangedEvent e) + private void onCurrentChange(ValueChangedEvent e) { if (e.NewValue == TabControl.Items.LastOrDefault()) return; - if (e.NewValue == index_page_string) + if (e.NewValue == IndexPageString) { ShowIndexPage?.Invoke(); return; @@ -73,8 +77,8 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = "wiki"; - Description = "knowledge base"; + Title = HeaderTitle; + Description = HeaderDescription; IconTexture = "Icons/Hexacons/wiki"; } } From 51742da89a91ba318f7d2cee74a3458ec87801e8 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 19:28:27 +0800 Subject: [PATCH 147/489] Add localisation for NewsHeader Co-authored-by: huoyaoyuan --- osu.Game/Localisation/NewsStrings.cs | 29 ++++++++++++++++++++++++++++ osu.Game/Overlays/News/NewsHeader.cs | 16 +++++++++------ 2 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Localisation/NewsStrings.cs diff --git a/osu.Game/Localisation/NewsStrings.cs b/osu.Game/Localisation/NewsStrings.cs new file mode 100644 index 0000000000..8cad6015cf --- /dev/null +++ b/osu.Game/Localisation/NewsStrings.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class NewsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.News"; + + /// + /// "frontpage" + /// + public static LocalisableString FrontPageString => new TranslatableString(getKey(@"front_page"), @"frontpage"); + + /// + /// "news" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"news"); + + /// + /// "join the real-time discussion" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"get up-to-date on community happenings"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 56c54425bd..0b0277f984 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -4,12 +4,16 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - private const string front_page_string = "frontpage"; + public static LocalisableString FrontPageString => NewsStrings.FrontPageString; + public static LocalisableString HeaderTitle => NewsStrings.HeaderTitle; + public static LocalisableString HeaderDescription => NewsStrings.HeaderDescription; public Action ShowFrontPage; @@ -17,7 +21,7 @@ namespace osu.Game.Overlays.News public NewsHeader() { - TabControl.AddItem(front_page_string); + TabControl.AddItem(FrontPageString); article.BindValueChanged(onArticleChanged, true); } @@ -28,7 +32,7 @@ namespace osu.Game.Overlays.News Current.BindValueChanged(e => { - if (e.NewValue == front_page_string) + if (e.NewValue == FrontPageString) ShowFrontPage?.Invoke(); }); } @@ -49,7 +53,7 @@ namespace osu.Game.Overlays.News } else { - Current.Value = front_page_string; + Current.Value = FrontPageString; } } @@ -61,8 +65,8 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = "news"; - Description = "get up-to-date on community happenings"; + Title = HeaderTitle; + Description = HeaderDescription; IconTexture = "Icons/Hexacons/news"; } } From 5b694ad28b1610b24a0d9303d3c39f18e0d14e4b Mon Sep 17 00:00:00 2001 From: huoyaoyuan Date: Sat, 17 Jul 2021 19:37:32 +0800 Subject: [PATCH 148/489] Replace string with LocalisableString --- osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs index 443b3dcf01..68faadeaf1 100644 --- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs +++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs @@ -4,15 +4,16 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { - public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader + public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader { - protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); + protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); - public class OverlayHeaderBreadcrumbControl : BreadcrumbControl + public class OverlayHeaderBreadcrumbControl : BreadcrumbControl { public OverlayHeaderBreadcrumbControl() { @@ -26,7 +27,7 @@ namespace osu.Game.Overlays AccentColour = colourProvider.Light2; } - protected override TabItem CreateTabItem(string value) => new ControlTabItem(value) + protected override TabItem CreateTabItem(LocalisableString value) => new ControlTabItem(value) { AccentColour = AccentColour, }; @@ -35,7 +36,7 @@ namespace osu.Game.Overlays { protected override float ChevronSize => 8; - public ControlTabItem(string value) + public ControlTabItem(LocalisableString value) : base(value) { RelativeSizeAxes = Axes.Y; From 22ff40fdd5f1362c511f3f2f31be0a1fe0e1c3b5 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 19:40:25 +0800 Subject: [PATCH 149/489] Fix broken WikiHeader --- osu.Game/Overlays/Wiki/WikiHeader.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 6b8cba48b4..2e15fbb566 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Wiki @@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = null; + Current.Value = string.Empty; TabControl.AddItem(index_page_string); @@ -51,7 +52,7 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } - private void onCurrentChange(ValueChangedEvent e) + private void onCurrentChange(ValueChangedEvent e) { if (e.NewValue == TabControl.Items.LastOrDefault()) return; From e3c10e39945fe7612a467a41ebca735cbd7bbc7a Mon Sep 17 00:00:00 2001 From: huoyaoyuan Date: Sat, 17 Jul 2021 19:40:32 +0800 Subject: [PATCH 150/489] Add missing LocalisableString --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index c447d7f609..f97880c7ca 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Graphics.Sprites; @@ -160,7 +161,12 @@ namespace osu.Game.Graphics.UserInterface Margin = new MarginPadding { Top = 5, Bottom = 5 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetLocalisableDescription() ?? value.ToString(), + Text = value switch{ + IHasDescription desc => desc?.Description, + Enum e => e.GetLocalisableDescription(), + LocalisableString l => l, + var other => other.ToString() + }, Font = OsuFont.GetFont(size: 14) }, Bar = new Box From 7859d02c5b4e0d65eef8b4ba9639b03c8a45af43 Mon Sep 17 00:00:00 2001 From: bdach Date: Sat, 17 Jul 2021 20:33:26 +0800 Subject: [PATCH 151/489] Allow null for breadcrumb control --- osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs | 10 +++++----- osu.Game/Overlays/Wiki/WikiHeader.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs index 68faadeaf1..0d383c374f 100644 --- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs +++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs @@ -9,11 +9,11 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { - public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader + public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader { - protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); + protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); - public class OverlayHeaderBreadcrumbControl : BreadcrumbControl + public class OverlayHeaderBreadcrumbControl : BreadcrumbControl { public OverlayHeaderBreadcrumbControl() { @@ -27,7 +27,7 @@ namespace osu.Game.Overlays AccentColour = colourProvider.Light2; } - protected override TabItem CreateTabItem(LocalisableString value) => new ControlTabItem(value) + protected override TabItem CreateTabItem(LocalisableString? value) => new ControlTabItem(value) { AccentColour = AccentColour, }; @@ -36,7 +36,7 @@ namespace osu.Game.Overlays { protected override float ChevronSize => 8; - public ControlTabItem(LocalisableString value) + public ControlTabItem(LocalisableString? value) : base(value) { RelativeSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 2e15fbb566..fb87486b4e 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = string.Empty; + Current.Value = null; TabControl.AddItem(index_page_string); @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } - private void onCurrentChange(ValueChangedEvent e) + private void onCurrentChange(ValueChangedEvent e) { if (e.NewValue == TabControl.Items.LastOrDefault()) return; From 5b4a1ef70a3294597979d5185919b51ac7a1f28d Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 20:40:59 +0800 Subject: [PATCH 152/489] Update test to match Breadcrumb change --- osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index 863fa48ddf..e7e6030c66 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Wiki; @@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual.Online private class TestHeader : WikiHeader { - public IReadOnlyList TabControlItems => TabControl.Items; + public IReadOnlyList TabControlItems => TabControl.Items; } } } From e6b8307b8e637479713accbc4a797a6a5818a7db Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 14:46:14 +0200 Subject: [PATCH 153/489] Localise `ProfileHeader` --- osu.Game/Overlays/Profile/ProfileHeader.cs | 33 +++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index c947ef0781..32bca68f0e 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -7,12 +7,14 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Overlays.Profile.Header; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile { - public class ProfileHeader : TabControlOverlayHeader + public class ProfileHeader : TabControlOverlayHeader { private UserCoverBackground coverContainer; @@ -27,8 +29,6 @@ namespace osu.Game.Overlays.Profile User.ValueChanged += e => updateDisplay(e.NewValue); - TabControl.AddItem("info"); - TabControl.AddItem("modding"); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Profile { public ProfileHeaderTitle() { - Title = "player info"; + Title = PageTitleStrings.MainUsersControllerDefault; IconTexture = "Icons/Hexacons/profile"; } } @@ -106,4 +106,29 @@ namespace osu.Game.Overlays.Profile protected override double LoadDelay => 0; } } + + [LocalisableEnum(typeof(ProfileHeaderTabEnumLocalisationMapper))] + public enum ProfileHeaderTab + { + Info, + Modding, + } + + public class ProfileHeaderTabEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(ProfileHeaderTab value) + { + switch (value) + { + case ProfileHeaderTab.Info: + return LayoutStrings.HeaderUsersShow; + + case ProfileHeaderTab.Modding: + return LayoutStrings.HeaderUsersModding; + + default: + return string.Empty; + } + } + } } From d9c7ea202672298d2ff31678218eae955d3f9271 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 14:57:05 +0200 Subject: [PATCH 154/489] Localise profile section titles. --- osu.Game/Overlays/Profile/ProfileSection.cs | 3 ++- osu.Game/Overlays/Profile/Sections/AboutSection.cs | 5 ++++- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/HistoricalSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/KudosuSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/MedalsSection.cs | 5 ++++- osu.Game/Overlays/Profile/Sections/RanksSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/RecentSection.cs | 4 +++- 8 files changed, 25 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index 21f7921da6..1a5f562fff 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Profile { public abstract class ProfileSection : Container { - public abstract string Title { get; } + public abstract LocalisableString Title { get; } public abstract string Identifier { get; } diff --git a/osu.Game/Overlays/Profile/Sections/AboutSection.cs b/osu.Game/Overlays/Profile/Sections/AboutSection.cs index 1bc01cfd9e..c224d2b1be 100644 --- a/osu.Game/Overlays/Profile/Sections/AboutSection.cs +++ b/osu.Game/Overlays/Profile/Sections/AboutSection.cs @@ -1,11 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.Profile.Sections { public class AboutSection : ProfileSection { - public override string Title => "me!"; + public override LocalisableString Title => UsersStrings.ShowExtraMeTitle; public override string Identifier => "me"; } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index c283de42f3..67aaf7d41e 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -1,14 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile.Sections.Beatmaps; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class BeatmapsSection : ProfileSection { - public override string Title => "Beatmaps"; + public override LocalisableString Title => UsersStrings.ShowExtraBeatmapsTitle; public override string Identifier => "beatmaps"; diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index 4fbb7fc7d7..09ca492aa9 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -2,15 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Overlays.Profile.Sections.Ranks; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class HistoricalSection : ProfileSection { - public override string Title => "Historical"; + public override LocalisableString Title => UsersStrings.ShowExtraHistoricalTitle; public override string Identifier => "historical"; diff --git a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs index a9e9952257..ffa7ef4eaf 100644 --- a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs +++ b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs @@ -3,12 +3,14 @@ using osu.Framework.Graphics; using osu.Game.Overlays.Profile.Sections.Kudosu; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class KudosuSection : ProfileSection { - public override string Title => "Kudosu!"; + public override LocalisableString Title => UsersStrings.ShowExtraKudosuTitle; public override string Identifier => "kudosu"; diff --git a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs index 575a2f2c19..3512333e27 100644 --- a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs @@ -1,11 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.Profile.Sections { public class MedalsSection : ProfileSection { - public override string Title => "Medals"; + public override LocalisableString Title => UsersStrings.ShowExtraMedalsTitle; public override string Identifier => "medals"; } diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index 33f7c2f71a..a7931c8675 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -3,12 +3,14 @@ using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Online.API.Requests; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class RanksSection : ProfileSection { - public override string Title => "Ranks"; + public override LocalisableString Title => UsersStrings.ShowExtraTopRanksTitle; public override string Identifier => "top_ranks"; diff --git a/osu.Game/Overlays/Profile/Sections/RecentSection.cs b/osu.Game/Overlays/Profile/Sections/RecentSection.cs index 1e6cfcc9fd..7a6536c3af 100644 --- a/osu.Game/Overlays/Profile/Sections/RecentSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RecentSection.cs @@ -1,13 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; using osu.Game.Overlays.Profile.Sections.Recent; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class RecentSection : ProfileSection { - public override string Title => "Recent"; + public override LocalisableString Title => UsersStrings.ShowExtraRecentActivityTitle; public override string Identifier => "recent_activity"; From ca1080dfb5d3cfa28319b69785c55e8c2278781c Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 21:16:57 +0800 Subject: [PATCH 155/489] use switch statement Co-authored-by: bdach --- .../Graphics/UserInterface/OsuTabControl.cs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index f97880c7ca..21e7a68e99 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -154,6 +154,26 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; + LocalisableString text; + switch (value) + { + case IHasDescription hasDescription: + text = hasDescription.GetDescription(); + break; + + case Enum e: + text = e.GetLocalisableDescription(); + break; + + case LocalisableString l: + text = l; + break; + + default: + text = value.ToString(); + break; + }; + Children = new Drawable[] { Text = new OsuSpriteText @@ -161,12 +181,7 @@ namespace osu.Game.Graphics.UserInterface Margin = new MarginPadding { Top = 5, Bottom = 5 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Text = value switch{ - IHasDescription desc => desc?.Description, - Enum e => e.GetLocalisableDescription(), - LocalisableString l => l, - var other => other.ToString() - }, + Text = text, Font = OsuFont.GetFont(size: 14) }, Bar = new Box From 4d276b114bbacdd9df278aeaa23ef59a956525ff Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:18:45 +0200 Subject: [PATCH 156/489] Localise profile header. --- .../Overlays/Profile/Header/Components/ExpandDetailsButton.cs | 3 ++- osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs | 3 ++- .../Overlays/Profile/Header/Components/LevelProgressBar.cs | 3 ++- .../Profile/Header/Components/MappingSubscribersButton.cs | 3 ++- .../Overlays/Profile/Header/Components/MessageUserButton.cs | 3 ++- .../Profile/Header/Components/OverlinedInfoContainer.cs | 3 ++- .../Profile/Header/Components/OverlinedTotalPlayTime.cs | 3 ++- .../Overlays/Profile/Header/Components/PreviousUsernames.cs | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs index 16b443875e..b4a5d5e31b 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Overlays.Profile.Header.Components @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly BindableBool DetailsVisible = new BindableBool(); - public override LocalisableString TooltipText => DetailsVisible.Value ? "collapse" : "expand"; + public override LocalisableString TooltipText => DetailsVisible.Value ? CommonStrings.ButtonsCollapse : CommonStrings.ButtonsExpand; private SpriteIcon icon; private Sample sampleOpen; diff --git a/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs index db94762efd..8f66120055 100644 --- a/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public override LocalisableString TooltipText => "followers"; + public override LocalisableString TooltipText => FriendsStrings.ButtonsDisabled; protected override IconUsage Icon => FontAwesome.Solid.User; diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index 528b05a80a..ed89d78a10 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -10,6 +10,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK.Graphics; @@ -26,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public LevelProgressBar() { - TooltipText = "progress to next level"; + TooltipText = UsersStrings.ShowStatsLevelProgress; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs index ae3d024fbf..5cdf3a5ef9 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public override LocalisableString TooltipText => "mapping subscribers"; + public override LocalisableString TooltipText => FollowsStrings.MappingFollowers; protected override IconUsage Icon => FontAwesome.Solid.Bell; diff --git a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs index 4c2cc768ce..07f1f1c3ed 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Online.API; using osu.Game.Online.Chat; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public override LocalisableString TooltipText => "send message"; + public override LocalisableString TooltipText => UsersStrings.CardSendMessage; [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs index 9f56a34aa6..8f1bbc4097 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK.Graphics; @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private readonly OsuSpriteText title; private readonly OsuSpriteText content; - public string Title + public LocalisableString Title { set => title.Text = value; } diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs index aa7cb8636a..1a40944632 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -31,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { InternalChild = info = new OverlinedInfoContainer { - Title = "Total Play Time", + Title = UsersStrings.ShowStatsPlayTime, LineColour = colourProvider.Highlight1, }; diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index 3cdf110090..14eeb4e5f0 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = @"formerly known as", + Text = UsersStrings.ShowPreviousUsernames, Font = OsuFont.GetFont(size: 10, italics: true) } }, From 306a34a80280dc608dff653666c2e7f5a2caaddf Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:21:30 +0200 Subject: [PATCH 157/489] Localise level badge tooltip. --- osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs index a0b8ef0f11..1deed1a748 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -19,13 +20,13 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public LocalisableString TooltipText { get; } + public LocalisableString TooltipText { get; private set; } private OsuSpriteText levelText; public LevelBadge() { - TooltipText = "level"; + TooltipText = UsersStrings.ShowStatsLevel("0"); } [BackgroundDependencyLoader] @@ -53,6 +54,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateLevel(User user) { levelText.Text = user?.Statistics?.Level.Current.ToString() ?? "0"; + TooltipText = UsersStrings.ShowStatsLevel(user?.Statistics?.Level.Current.ToString()); } } } From a0c6945f8fd320e3d23ab8b07706dc66b01fcaf6 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:25:45 +0200 Subject: [PATCH 158/489] Localise user graph. --- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 5 +++-- osu.Game/Overlays/Profile/UserGraph.cs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index ad91e491ef..6bf356c0ff 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -27,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = "No recent plays", + Text = UsersStrings.ShowExtraUnranked, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular) }); } @@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private class RankGraphTooltip : UserGraphTooltip { public RankGraphTooltip() - : base("Global Ranking") + : base(UsersStrings.ShowRankGlobalSimple) { } diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index cdfd722d68..b7a08b6c5e 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -211,7 +212,7 @@ namespace osu.Game.Overlays.Profile protected readonly OsuSpriteText Counter, BottomText; private readonly Box background; - protected UserGraphTooltip(string tooltipCounterName) + protected UserGraphTooltip(LocalisableString tooltipCounterName) { AutoSizeAxes = Axes.Both; Masking = true; From 2cfec1dc326af464ccb324a3092ac54673b0dad2 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:26:18 +0200 Subject: [PATCH 159/489] Localise osu!supporter badge. --- osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index 9a43997030..77f0378762 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Header.Components { @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private readonly FillFlowContainer iconContainer; private readonly CircularContainer content; - public LocalisableString TooltipText => "osu!supporter"; + public LocalisableString TooltipText => UsersStrings.ShowIsSupporter; public int SupportLevel { From c6a27e4baaf84cf9e07df687bbd83b6a90c5bb36 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:27:40 +0200 Subject: [PATCH 160/489] Localise `CentreHeaderContainer`. --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 62ebee7677..4195b0b2f1 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; @@ -119,12 +120,12 @@ namespace osu.Game.Overlays.Profile.Header { hiddenDetailGlobal = new OverlinedInfoContainer { - Title = "Global Ranking", + Title = UsersStrings.ShowRankGlobalSimple, LineColour = colourProvider.Highlight1 }, hiddenDetailCountry = new OverlinedInfoContainer { - Title = "Country Ranking", + Title = UsersStrings.ShowRankCountrySimple, LineColour = colourProvider.Highlight1 }, } From 213e3c0716e8706565489a4cd3896e08ca0a5383 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:29:34 +0200 Subject: [PATCH 161/489] Localise `DetailHeaderContainer` --- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 574aef02fd..6214e504b0 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osu.Game.Users; using osuTK; @@ -100,7 +101,7 @@ namespace osu.Game.Overlays.Profile.Header }, medalInfo = new OverlinedInfoContainer { - Title = "Medals", + Title = UsersStrings.ShowStatsMedals, LineColour = colours.GreenLight, }, ppInfo = new OverlinedInfoContainer @@ -151,12 +152,12 @@ namespace osu.Game.Overlays.Profile.Header { detailGlobalRank = new OverlinedInfoContainer(true, 110) { - Title = "Global Ranking", + Title = UsersStrings.ShowRankGlobalSimple, LineColour = colourProvider.Highlight1, }, detailCountryRank = new OverlinedInfoContainer(false, 110) { - Title = "Country Ranking", + Title = UsersStrings.ShowRankCountrySimple, LineColour = colourProvider.Highlight1, }, } From c6bc95767dc5e2ac3e24596a6d99fd06e3d53ed1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 22:31:47 +0900 Subject: [PATCH 162/489] Simplify popover hide logic and add test coverage --- .../TestSceneMultiplayerLoungeSubScreen.cs | 12 +++++++++++ .../Lounge/Components/RoomsContainer.cs | 7 ------- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 20 ++++++++++++------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index bde9ea89ed..de46d9e25a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -52,6 +52,18 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room join password correct", () => lastJoinedPassword == null); } + [Test] + public void TestPopoverHidesOnLeavingScreen() + { + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + + AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any()); + AddStep("exit screen", () => Stack.Exit()); + AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any()); + } + [Test] public void TestJoinRoomWithPassword() { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 0910ff1a7a..07e412ee75 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -7,7 +7,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -239,11 +238,5 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (roomManager != null) roomManager.RoomsUpdated -= updateSorting; } - - public void HideAnyPopovers() - { - // must be called on a child of the PopoverContainer due to parent traversal not considering self. - roomFlow.HidePopover(); - } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index dd6106b868..f43109c4fa 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -6,6 +6,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -151,24 +152,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge onReturning(); } - private void onReturning() - { - filter.HoldFocus = true; - } - public override bool OnExiting(IScreen next) { - filter.HoldFocus = false; + onLeaving(); return base.OnExiting(next); } public override void OnSuspending(IScreen next) { + onLeaving(); base.OnSuspending(next); + } + + private void onReturning() + { + filter.HoldFocus = true; + } + + private void onLeaving() + { filter.HoldFocus = false; // ensure any password prompt is dismissed. - roomsContainer.HideAnyPopovers(); + this.HidePopover(); } public void Join(Room room, string password) From d36842aa15a47a5dd252802970ed9e465fb4552a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:32:28 +0200 Subject: [PATCH 163/489] Localise `TopHeaderContainer` --- .../Profile/Header/TopHeaderContainer.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index d751424367..b64dba62e3 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -7,11 +7,13 @@ 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.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; @@ -179,19 +181,19 @@ namespace osu.Game.Overlays.Profile.Header if (user?.Statistics != null) { - userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Hit Accuracy", user.Statistics.DisplayAccuracy)); - userStats.Add(new UserStatsLine("Play Count", user.Statistics.PlayCount.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Total Score", user.Statistics.TotalScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Total Hits", user.Statistics.TotalHits.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Maximum Combo", user.Statistics.MaxCombo.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Replays Watched by Others", user.Statistics.ReplaysWatched.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsHitAccuracy, user.Statistics.DisplayAccuracy)); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToString("#,##0"))); } } private class UserStatsLine : Container { - public UserStatsLine(string left, string right) + public UserStatsLine(LocalisableString left, string right) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From f4eeb9139ed3784b9ada9f7b13cc307b839f84ab Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 21:37:58 +0800 Subject: [PATCH 164/489] Correct code style --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 21e7a68e99..3572ea5c31 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -155,6 +155,7 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Y; LocalisableString text; + switch (value) { case IHasDescription hasDescription: @@ -172,7 +173,7 @@ namespace osu.Game.Graphics.UserInterface default: text = value.ToString(); break; - }; + } Children = new Drawable[] { From 5bb45c7f84fb4b30e310a1723fb2e8dfc0753ce1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:45:17 +0200 Subject: [PATCH 165/489] Localise beatmap section. --- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 5 +++-- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 10 +++++----- .../Profile/Sections/PaginatedProfileSubsection.cs | 9 +++++---- .../Overlays/Profile/Sections/ProfileSubsection.cs | 5 +++-- .../Profile/Sections/ProfileSubsectionHeader.cs | 5 +++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index fe9c710bcc..af1ed9d529 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -19,8 +20,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private const float panel_padding = 10f; private readonly BeatmapSetType type; - public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string headerText) - : base(user, headerText) + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) + : base(user, headerText, null) { this.type = type; ItemsPerPage = 6; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 67aaf7d41e..b8fbe73c0c 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -18,11 +18,11 @@ namespace osu.Game.Overlays.Profile.Sections { Children = new[] { - new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, "Loved Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps") + new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, UsersStrings.ShowExtraBeatmapsFavouriteTitle), + new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, UsersStrings.ShowExtraBeatmapsRankedTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, UsersStrings.ShowExtraBeatmapsLovedTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, UsersStrings.ShowExtraBeatmapsPendingTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, UsersStrings.ShowExtraBeatmapsGraveyardTitle) }; } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index e237b43b2e..25c72a24da 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -36,9 +37,9 @@ namespace osu.Game.Overlays.Profile.Sections private ShowMoreButton moreButton; private OsuSpriteText missing; - private readonly string missingText; + private readonly LocalisableString? missingText; - protected PaginatedProfileSubsection(Bindable user, string headerText = "", string missingText = "") + protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText) : base(user, headerText, CounterVisibilityState.AlwaysVisible) { this.missingText = missingText; @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.Profile.Sections missing = new OsuSpriteText { Font = OsuFont.GetFont(size: 15), - Text = missingText, + Text = missingText ?? string.Empty, Alpha = 0, } } @@ -114,7 +115,7 @@ namespace osu.Game.Overlays.Profile.Sections moreButton.Hide(); moreButton.IsLoading = false; - if (!string.IsNullOrEmpty(missingText)) + if (missingText.HasValue) missing.Show(); return; diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs index 3e331f85e9..a9d2448abf 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Users; using JetBrains.Annotations; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -14,12 +15,12 @@ namespace osu.Game.Overlays.Profile.Sections { protected readonly Bindable User = new Bindable(); - private readonly string headerText; + private readonly LocalisableString headerText; private readonly CounterVisibilityState counterVisibilityState; private ProfileSubsectionHeader header; - protected ProfileSubsection(Bindable user, string headerText = "", CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) + protected ProfileSubsection(Bindable user, LocalisableString headerText, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) { this.headerText = headerText; this.counterVisibilityState = counterVisibilityState; diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs index 5858cebe89..408cb00770 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osuTK; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -24,12 +25,12 @@ namespace osu.Game.Overlays.Profile.Sections set => current.Current = value; } - private readonly string text; + private readonly LocalisableString text; private readonly CounterVisibilityState counterState; private CounterPill counterPill; - public ProfileSubsectionHeader(string text, CounterVisibilityState counterState) + public ProfileSubsectionHeader(LocalisableString text, CounterVisibilityState counterState) { this.text = text; this.counterState = counterState; From d17f6589857a1448bdb445dccb3cb0d916a137ff Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:50:56 +0200 Subject: [PATCH 166/489] Localise Recent section. --- .../Sections/Recent/PaginatedRecentActivityContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index d7101a8147..04c8d7483b 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -10,13 +10,14 @@ using osu.Game.Online.API; using System.Collections.Generic; using osuTK; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections.Recent { public class PaginatedRecentActivityContainer : PaginatedProfileSubsection { public PaginatedRecentActivityContainer(Bindable user) - : base(user, missingText: "This user hasn't done anything notable recently!") + : base(user, string.Empty, EventsStrings.Empty) { ItemsPerPage = 10; } From fbbf8ce5a3950f7a3da6a08ec61c64c4f5a2b1f7 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:53:24 +0200 Subject: [PATCH 167/489] Localise Ranks section. --- .../Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 +- .../Overlays/Profile/Sections/PaginatedProfileSubsection.cs | 2 +- .../Profile/Sections/Ranks/PaginatedScoreContainer.cs | 3 ++- osu.Game/Overlays/Profile/Sections/RanksSection.cs | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index af1ed9d529..ec64371a5d 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private readonly BeatmapSetType type; public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) - : base(user, headerText, null) + : base(user, headerText) { this.type = type; ItemsPerPage = 6; diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 25c72a24da..4a37c94abe 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections private OsuSpriteText missing; private readonly LocalisableString? missingText; - protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText) + protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText = null) : base(user, headerText, CounterVisibilityState.AlwaysVisible) { this.missingText = missingText; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 720cd4a3db..7c04b331c2 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -11,6 +11,7 @@ using osu.Game.Online.API.Requests.Responses; using System.Collections.Generic; using osu.Game.Online.API; using osu.Framework.Allocation; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Ranks { @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { private readonly ScoreType type; - public PaginatedScoreContainer(ScoreType type, Bindable user, string headerText) + public PaginatedScoreContainer(ScoreType type, Bindable user, LocalisableString headerText) : base(user, headerText) { this.type = type; diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index a7931c8675..5e0648dbb1 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -18,8 +18,8 @@ namespace osu.Game.Overlays.Profile.Sections { Children = new[] { - new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance"), - new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks") + new PaginatedScoreContainer(ScoreType.Best, User, UsersStrings.ShowExtraTopRanksBestTitle), + new PaginatedScoreContainer(ScoreType.Firsts, User, UsersStrings.ShowExtraTopRanksFirstTitle) }; } } From 2f3ed4a4ab080d7758afc6d2786a7d1118b4f736 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:13:33 +0200 Subject: [PATCH 168/489] Fix `PaginatedProfileSubsection` ctor arguments --- .../Overlays/Profile/Sections/PaginatedProfileSubsection.cs | 2 +- osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 4a37c94abe..d60243cd0a 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections private OsuSpriteText missing; private readonly LocalisableString? missingText; - protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText = null) + protected PaginatedProfileSubsection(Bindable user, LocalisableString? headerText = null, LocalisableString? missingText = null) : base(user, headerText, CounterVisibilityState.AlwaysVisible) { this.missingText = missingText; diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs index a9d2448abf..5a17f0d8bb 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs @@ -20,9 +20,9 @@ namespace osu.Game.Overlays.Profile.Sections private ProfileSubsectionHeader header; - protected ProfileSubsection(Bindable user, LocalisableString headerText, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) + protected ProfileSubsection(Bindable user, LocalisableString? headerText = null, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) { - this.headerText = headerText; + this.headerText = headerText ?? string.Empty; this.counterVisibilityState = counterVisibilityState; User.BindTo(user); } @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Sections { header = new ProfileSubsectionHeader(headerText, counterVisibilityState) { - Alpha = string.IsNullOrEmpty(headerText) ? 0 : 1 + Alpha = string.IsNullOrEmpty(headerText.ToString()) ? 0 : 1 }, CreateContent() }; From 148eb890fff5707f28d586121db754561943d81d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:20:37 +0200 Subject: [PATCH 169/489] Localise Historical section. --- .../Sections/Historical/ChartProfileSubsection.cs | 5 +++-- .../Sections/Historical/DrawableMostPlayedBeatmap.cs | 3 ++- .../Historical/PaginatedMostPlayedBeatmapContainer.cs | 3 ++- .../Sections/Historical/PlayHistorySubsection.cs | 6 ++++-- .../Profile/Sections/Historical/ProfileLineChart.cs | 3 ++- .../Profile/Sections/Historical/ReplaysSubsection.cs | 6 ++++-- .../Profile/Sections/Historical/UserHistoryGraph.cs | 11 ++++++----- .../Overlays/Profile/Sections/HistoricalSection.cs | 2 +- 8 files changed, 24 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index a48036dcbb..986b3d9874 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Users; using static osu.Game.Users.User; @@ -18,9 +19,9 @@ namespace osu.Game.Overlays.Profile.Sections.Historical /// /// Text describing the value being plotted on the graph, which will be displayed as a prefix to the value in the history graph tooltip. /// - protected abstract string GraphCounterName { get; } + protected abstract LocalisableString GraphCounterName { get; } - protected ChartProfileSubsection(Bindable user, string headerText) + protected ChartProfileSubsection(Bindable user, LocalisableString headerText) : base(user, headerText) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 6f1869966a..a419bef233 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections.Historical { @@ -143,7 +144,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class PlayCountText : CompositeDrawable, IHasTooltip { - public LocalisableString TooltipText => "times played"; + public LocalisableString TooltipText => UsersStrings.ShowExtraHistoricalMostPlayedCount; public PlayCountText(int playCount) { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index eeb14e5e4f..d0979526da 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Sections.Historical @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public class PaginatedMostPlayedBeatmapContainer : PaginatedProfileSubsection { public PaginatedMostPlayedBeatmapContainer(Bindable user) - : base(user, "Most Played Beatmaps") + : base(user, UsersStrings.ShowExtraHistoricalMostPlayedTitle) { ItemsPerPage = 5; } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs index dfd29db693..83c005970e 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using static osu.Game.Users.User; @@ -9,10 +11,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class PlayHistorySubsection : ChartProfileSubsection { - protected override string GraphCounterName => "Plays"; + protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalMonthlyPlaycountsCountLabel; public PlayHistorySubsection(Bindable user) - : base(user, "Play History") + : base(user, UsersStrings.ShowExtraHistoricalMonthlyPlaycountsTitle) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index eb5deb2802..af39251781 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; using osuTK; using static osu.Game.Users.User; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Historical { @@ -42,7 +43,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private readonly Container rowLinesContainer; private readonly Container columnLinesContainer; - public ProfileLineChart(string graphCounterName) + public ProfileLineChart(LocalisableString graphCounterName) { RelativeSizeAxes = Axes.X; Height = 250; diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs index 1c28306f17..76d5f73bd7 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using static osu.Game.Users.User; @@ -9,10 +11,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class ReplaysSubsection : ChartProfileSubsection { - protected override string GraphCounterName => "Replays Watched"; + protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalReplaysWatchedCountsCountLabel; public ReplaysSubsection(Bindable user) - : base(user, "Replays Watched History") + : base(user, UsersStrings.ShowExtraHistoricalReplaysWatchedCountsTitle) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index 52831b4243..d626c63fed 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -5,13 +5,14 @@ using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using osu.Framework.Localisation; using static osu.Game.Users.User; namespace osu.Game.Overlays.Profile.Sections.Historical { public class UserHistoryGraph : UserGraph { - private readonly string tooltipCounterName; + private readonly LocalisableString tooltipCounterName; [CanBeNull] public UserHistoryCount[] Values @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical set => Data = value?.Select(v => new KeyValuePair(v.Date, v.Count)).ToArray(); } - public UserHistoryGraph(string tooltipCounterName) + public UserHistoryGraph(LocalisableString tooltipCounterName) { this.tooltipCounterName = tooltipCounterName; } @@ -40,9 +41,9 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected class HistoryGraphTooltip : UserGraphTooltip { - private readonly string tooltipCounterName; + private readonly LocalisableString tooltipCounterName; - public HistoryGraphTooltip(string tooltipCounterName) + public HistoryGraphTooltip(LocalisableString tooltipCounterName) : base(tooltipCounterName) { this.tooltipCounterName = tooltipCounterName; @@ -61,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class TooltipDisplayContent { - public string Name; + public LocalisableString Name; public string Count; public string Date; } diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index 09ca492aa9..cba25c0a8b 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections { new PlayHistorySubsection(User), new PaginatedMostPlayedBeatmapContainer(User), - new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)"), + new PaginatedScoreContainer(ScoreType.Recent, User, UsersStrings.ShowExtraHistoricalRecentPlaysTitle), new ReplaysSubsection(User) }; } From 2545275f7102e0607d81eaced1c00db66efb08c1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:29:09 +0200 Subject: [PATCH 170/489] Partly localise Kudosu section. --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index cdb24b784c..37de669b3b 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -12,6 +12,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Users; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Kudosu { @@ -37,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private class CountTotal : CountSection { public CountTotal() - : base("Total Kudosu Earned") + : base(UsersStrings.ShowExtraKudosuTotal) { DescriptionText.AddText("Based on how much of a contribution the user has made to beatmap moderation. See "); DescriptionText.AddLink("this page", "https://osu.ppy.sh/wiki/Kudosu"); @@ -56,7 +58,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu set => valueText.Text = value.ToString("N0"); } - public CountSection(string header) + public CountSection(LocalisableString header) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From a7c280508f9480141da9b324edddf613e7c7c022 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:40:37 +0200 Subject: [PATCH 171/489] Throw instead of silently returning. --- osu.Game/Overlays/Profile/ProfileHeader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 32bca68f0e..083725418e 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -127,7 +128,7 @@ namespace osu.Game.Overlays.Profile return LayoutStrings.HeaderUsersModding; default: - return string.Empty; + throw new ArgumentOutOfRangeException(nameof(value), value, null); } } } From 77d8f240f8e33d75f2f92caf0c6d7e12b7465463 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:41:42 +0200 Subject: [PATCH 172/489] Use ctor default values. --- .../Profile/Sections/Recent/PaginatedRecentActivityContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 04c8d7483b..db2e6bc1e0 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent public class PaginatedRecentActivityContainer : PaginatedProfileSubsection { public PaginatedRecentActivityContainer(Bindable user) - : base(user, string.Empty, EventsStrings.Empty) + : base(user, missingText: EventsStrings.Empty) { ItemsPerPage = 10; } From 28845364a3380980a101f984f38312d792f9f376 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:52:35 +0200 Subject: [PATCH 173/489] Localise score weighting. --- .../Profile/Sections/Ranks/DrawableProfileWeightedScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs index 3afa79e59e..63305d004c 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osuTK; @@ -51,7 +52,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks new OsuSpriteText { Font = OsuFont.GetFont(size: 12), - Text = $@"weighted {weight:0%}" + Text = UsersStrings.ShowExtraTopRanksPpWeight(weight.ToString("0%")) } } }; From 2c26248042e58a6db18531fbbe00980e7fdada8f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 17:36:49 +0200 Subject: [PATCH 174/489] Localise missing text of `PaginatedKudosuHistoryContainer`. --- .../Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index 008d89d881..76cd7ed722 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -8,13 +8,14 @@ using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API; using System.Collections.Generic; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections.Kudosu { public class PaginatedKudosuHistoryContainer : PaginatedProfileSubsection { public PaginatedKudosuHistoryContainer(Bindable user) - : base(user, missingText: "This user hasn't received any kudosu!") + : base(user, missingText: UsersStrings.ShowExtraKudosuEntryEmpty) { ItemsPerPage = 5; } From 9a2fb8ca6c26daf8cb926f8744cc550496e7e5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 17 Jul 2021 18:06:45 +0200 Subject: [PATCH 175/489] Add test coverage for null mod on seeding screen --- .../Screens/TestSceneSeedingScreen.cs | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs index d414d8e36e..a18e73e38f 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs @@ -1,9 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Tournament.Models; +using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.TeamIntro; namespace osu.Game.Tournament.Tests.Screens @@ -11,16 +15,41 @@ namespace osu.Game.Tournament.Tests.Screens public class TestSceneSeedingScreen : TournamentTestScene { [Cached] - private readonly LadderInfo ladder = new LadderInfo(); - - [BackgroundDependencyLoader] - private void load() + private readonly LadderInfo ladder = new LadderInfo { - Add(new SeedingScreen + Teams = + { + new TournamentTeam + { + FullName = { Value = @"Japan" }, + Acronym = { Value = "JPN" }, + SeedingResults = + { + new SeedingResult + { + // Mod intentionally left blank. + Seed = { Value = 4 } + }, + new SeedingResult + { + Mod = { Value = "DT" }, + Seed = { Value = 8 } + } + } + } + } + }; + + [Test] + public void TestBasic() + { + AddStep("create seeding screen", () => Add(new SeedingScreen { FillMode = FillMode.Fit, FillAspectRatio = 16 / 9f - }); + })); + + AddStep("set team to Japan", () => this.ChildrenOfType().Single().Current.Value = ladder.Teams.Single()); } } } From 714255e6d4059e72e46083328367e1d534726581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 17 Jul 2021 17:53:58 +0200 Subject: [PATCH 176/489] Fix seeding screen crashing on seedings with null mod --- .../Screens/TeamIntro/SeedingScreen.cs | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs index 4f66d89b7f..2f82578586 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs @@ -179,44 +179,48 @@ namespace osu.Game.Tournament.Screens.TeamIntro [BackgroundDependencyLoader] private void load(TextureStore textures) { + FillFlowContainer row; + InternalChildren = new Drawable[] { - new FillFlowContainer + row = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(5), - Children = new Drawable[] - { - new Sprite - { - Texture = textures.Get($"mods/{mods.ToLower()}"), - Scale = new Vector2(0.5f) - }, - new Container - { - Size = new Vector2(50, 16), - CornerRadius = 10, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = TournamentGame.ELEMENT_BACKGROUND_COLOUR, - }, - new TournamentSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = seeding.ToString("#,0"), - Colour = TournamentGame.ELEMENT_FOREGROUND_COLOUR - }, - } - }, - } }, }; + + if (!string.IsNullOrEmpty(mods)) + { + row.Add(new Sprite + { + Texture = textures.Get($"mods/{mods.ToLower()}"), + Scale = new Vector2(0.5f) + }); + } + + row.Add(new Container + { + Size = new Vector2(50, 16), + CornerRadius = 10, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = TournamentGame.ELEMENT_BACKGROUND_COLOUR, + }, + new TournamentSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = seeding.ToString("#,0"), + Colour = TournamentGame.ELEMENT_FOREGROUND_COLOUR + }, + } + }); } } } From 1b4bff0d9fa52dec5b18f33158b4201bcb1ed2e2 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 00:28:32 +0800 Subject: [PATCH 177/489] Optimize code style Co-authored-by: frenzibyte Co-authored-by: bdach --- osu.Game/Localisation/NewsStrings.cs | 10 ---------- osu.Game/Localisation/WikiStrings.cs | 29 ---------------------------- osu.Game/Overlays/News/NewsHeader.cs | 11 ++++------- osu.Game/Overlays/Wiki/WikiHeader.cs | 10 ++++------ 4 files changed, 8 insertions(+), 52 deletions(-) delete mode 100644 osu.Game/Localisation/WikiStrings.cs diff --git a/osu.Game/Localisation/NewsStrings.cs b/osu.Game/Localisation/NewsStrings.cs index 8cad6015cf..4f1fe6e776 100644 --- a/osu.Game/Localisation/NewsStrings.cs +++ b/osu.Game/Localisation/NewsStrings.cs @@ -9,16 +9,6 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.News"; - /// - /// "frontpage" - /// - public static LocalisableString FrontPageString => new TranslatableString(getKey(@"front_page"), @"frontpage"); - - /// - /// "news" - /// - public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"news"); - /// /// "join the real-time discussion" /// diff --git a/osu.Game/Localisation/WikiStrings.cs b/osu.Game/Localisation/WikiStrings.cs deleted file mode 100644 index 6176fd4e85..0000000000 --- a/osu.Game/Localisation/WikiStrings.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class WikiStrings - { - private const string prefix = @"osu.Game.Resources.Localisation.Wiki"; - - /// - /// "index" - /// - public static LocalisableString IndexPageString => new TranslatableString(getKey(@"index_page"), @"index"); - - /// - /// "wiki" - /// - public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"wiki"); - - /// - /// "knowledge base" - /// - public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"knowledge base"); - - private static string getKey(string key) => $"{prefix}:{key}"; - } -} diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 0b0277f984..7f08300912 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -5,16 +5,13 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - public static LocalisableString FrontPageString => NewsStrings.FrontPageString; - public static LocalisableString HeaderTitle => NewsStrings.HeaderTitle; - public static LocalisableString HeaderDescription => NewsStrings.HeaderDescription; - + public LocalisableString FrontPageString => osu.Game.Resources.Localisation.Web.NewsStrings.IndexTitleInfo; public Action ShowFrontPage; private readonly Bindable article = new Bindable(); @@ -65,8 +62,8 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = HeaderTitle; - Description = HeaderDescription; + Title = LayoutStrings.MenuHomeNewsIndex; + Description = osu.Game.Localisation.NewsStrings.HeaderDescription; IconTexture = "Icons/Hexacons/news"; } } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 4bec42b6ce..f24d59a8d3 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -6,17 +6,15 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; -using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Wiki { public class WikiHeader : BreadcrumbControlOverlayHeader { private const string index_path = "Main_Page"; - public static LocalisableString IndexPageString => WikiStrings.IndexPageString; - public static LocalisableString HeaderTitle => WikiStrings.HeaderTitle; - public static LocalisableString HeaderDescription => WikiStrings.HeaderDescription; + public LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; public readonly Bindable WikiPageData = new Bindable(); @@ -77,8 +75,8 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = HeaderTitle; - Description = HeaderDescription; + Title = LayoutStrings.MenuHelpGetWiki; + Description = PageTitleStrings.MainWikiControllerDefault; IconTexture = "Icons/Hexacons/wiki"; } } From 53fe61504cbc53fd77207a4caec081cc76855d63 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 01:35:54 +0800 Subject: [PATCH 178/489] Add localisation for ChangelogHeader --- osu.Game/Localisation/ChangelogStrings.cs | 19 +++++++++++++++++++ .../Overlays/Changelog/ChangelogHeader.cs | 14 ++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Localisation/ChangelogStrings.cs diff --git a/osu.Game/Localisation/ChangelogStrings.cs b/osu.Game/Localisation/ChangelogStrings.cs new file mode 100644 index 0000000000..b720670ded --- /dev/null +++ b/osu.Game/Localisation/ChangelogStrings.cs @@ -0,0 +1,19 @@ +// 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 ChangelogStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.Changelog"; + + /// + /// "track recent dev updates in the osu! ecosystem" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"track recent dev updates in the osu! ecosystem"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index f4be4328e7..c1c81046ed 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -9,7 +9,9 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Changelog { @@ -21,16 +23,16 @@ namespace osu.Game.Overlays.Changelog public ChangelogUpdateStreamControl Streams; - private const string listing_string = "listing"; + public LocalisableString ListingString => LayoutStrings.HeaderChangelogIndex; private Box streamsBackground; public ChangelogHeader() { - TabControl.AddItem(listing_string); + TabControl.AddItem(ListingString); Current.ValueChanged += e => { - if (e.NewValue == listing_string) + if (e.NewValue == ListingString) ListingSelected?.Invoke(); }; @@ -63,7 +65,7 @@ namespace osu.Game.Overlays.Changelog } else { - Current.Value = listing_string; + Current.Value = ListingString; Streams.Current.Value = null; } } @@ -114,8 +116,8 @@ namespace osu.Game.Overlays.Changelog { public ChangelogHeaderTitle() { - Title = "changelog"; - Description = "track recent dev updates in the osu! ecosystem"; + Title = LayoutStrings.MenuHomeChangelogIndex; + Description = osu.Game.Localisation.ChangelogStrings.HeaderDescription; IconTexture = "Icons/Hexacons/devtools"; } } From 224c37bdc3e67e0f110c3c679080dddcaa0113b6 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:15:14 +0800 Subject: [PATCH 179/489] Add localisation for RankingOverlay --- .../Rankings/RankingsOverlayHeader.cs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 92e22f5873..4766d1ab3c 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -1,9 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Game.Rulesets; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings @@ -29,13 +32,14 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = "ranking"; + Title = LayoutStrings.MenuRankingsDefault; Description = "find out who's the best right now"; IconTexture = "Icons/Hexacons/rankings"; } } } + [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] public enum RankingsScope { Performance, @@ -43,4 +47,29 @@ namespace osu.Game.Overlays.Rankings Score, Country } + + public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsScope value) + { + switch (value) + { + case RankingsScope.Performance: + return LayoutStrings.MenuRankingsIndex; + + case RankingsScope.Spotlights: + return LayoutStrings.MenuRankingsCharts; + + case RankingsScope.Score: + return LayoutStrings.MenuRankingsScore; + + case RankingsScope.Country: + return LayoutStrings.MenuRankingsCountry; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } + } From 93e79d122fe04e0ab5080eb8e042c313ac27c17d Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:23:12 +0800 Subject: [PATCH 180/489] Move strings together --- ...Strings.cs => HeaderDescriptionStrings.cs} | 11 ++++++++--- osu.Game/Localisation/NewsStrings.cs | 19 ------------------- .../Overlays/Changelog/ChangelogHeader.cs | 3 ++- osu.Game/Overlays/News/NewsHeader.cs | 3 ++- 4 files changed, 12 insertions(+), 24 deletions(-) rename osu.Game/Localisation/{ChangelogStrings.cs => HeaderDescriptionStrings.cs} (50%) delete mode 100644 osu.Game/Localisation/NewsStrings.cs diff --git a/osu.Game/Localisation/ChangelogStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs similarity index 50% rename from osu.Game/Localisation/ChangelogStrings.cs rename to osu.Game/Localisation/HeaderDescriptionStrings.cs index b720670ded..6dbc28a619 100644 --- a/osu.Game/Localisation/ChangelogStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -5,14 +5,19 @@ using osu.Framework.Localisation; namespace osu.Game.Localisation { - public static class ChangelogStrings + public static class HeaderDescriptionStrings { - private const string prefix = @"osu.Game.Resources.Localisation.Changelog"; + private const string prefix = @"osu.Game.Resources.Localisation.HeaderDescription"; /// /// "track recent dev updates in the osu! ecosystem" /// - public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"track recent dev updates in the osu! ecosystem"); + public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + + /// + /// "get up-to-date on community happenings" + /// + public static LocalisableString News => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); private static string getKey(string key) => $"{prefix}:{key}"; } diff --git a/osu.Game/Localisation/NewsStrings.cs b/osu.Game/Localisation/NewsStrings.cs deleted file mode 100644 index 4f1fe6e776..0000000000 --- a/osu.Game/Localisation/NewsStrings.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class NewsStrings - { - private const string prefix = @"osu.Game.Resources.Localisation.News"; - - /// - /// "join the real-time discussion" - /// - public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"get up-to-date on community happenings"); - - private static string getKey(string key) => $"{prefix}:{key}"; - } -} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index c1c81046ed..a282c77bd6 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; @@ -117,7 +118,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeaderTitle() { Title = LayoutStrings.MenuHomeChangelogIndex; - Description = osu.Game.Localisation.ChangelogStrings.HeaderDescription; + Description = HeaderDescriptionStrings.Changelog; IconTexture = "Icons/Hexacons/devtools"; } } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 7f08300912..b6e49811ad 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.News @@ -63,7 +64,7 @@ namespace osu.Game.Overlays.News public NewsHeaderTitle() { Title = LayoutStrings.MenuHomeNewsIndex; - Description = osu.Game.Localisation.NewsStrings.HeaderDescription; + Description = HeaderDescriptionStrings.News; IconTexture = "Icons/Hexacons/news"; } } From 3b48975f1e7f1b0ed760c56a964bdc8aea75ed34 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:32:23 +0800 Subject: [PATCH 181/489] Add localisation for RankingOverlayHeader --- osu.Game/Localisation/HeaderDescriptionStrings.cs | 5 +++++ osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs index 6dbc28a619..88ee4a0d75 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -14,6 +14,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + /// + /// "find out who's the best right now" + /// + public static LocalisableString Rankings => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + /// /// "get up-to-date on community happenings" /// diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 4766d1ab3c..92750d8806 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Rulesets; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -33,7 +34,7 @@ namespace osu.Game.Overlays.Rankings public RankingsTitle() { Title = LayoutStrings.MenuRankingsDefault; - Description = "find out who's the best right now"; + Description = HeaderDescriptionStrings.Rankings; IconTexture = "Icons/Hexacons/rankings"; } } From f588607ed978aeb333e3f06e69ed833b7b2525f6 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:38:15 +0800 Subject: [PATCH 182/489] Add localisation for BeatmapListingHeader --- osu.Game/Localisation/HeaderDescriptionStrings.cs | 5 +++++ osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs index 88ee4a0d75..c84822de53 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -9,6 +9,11 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.HeaderDescription"; + /// + /// "browse for new beatmaps" + /// + public static LocalisableString BeatmapListing => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + /// /// "track recent dev updates in the osu! ecosystem" /// diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 6a9a71210a..48ecbce7c1 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.BeatmapListing { public class BeatmapListingHeader : OverlayHeader @@ -11,8 +14,8 @@ namespace osu.Game.Overlays.BeatmapListing { public BeatmapListingTitle() { - Title = "beatmap listing"; - Description = "browse for new beatmaps"; + Title = PageTitleStrings.MainBeatmapsetsControllerIndex; + Description = HeaderDescriptionStrings.BeatmapListing; IconTexture = "Icons/Hexacons/beatmap"; } } From f80b18377775e3bf52b4370d0bd7459dc383112c Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:44:09 +0800 Subject: [PATCH 183/489] Add localisation for DashboardOverlayHeader --- osu.Game/Localisation/HeaderDescriptionStrings.cs | 5 +++++ osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs index c84822de53..6ed703191a 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -19,6 +19,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + /// + /// "view your friends and other information" + /// + public static LocalisableString Dashboard => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + /// /// "find out who's the best right now" /// diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 056d4ad6f7..284e95ad84 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Dashboard public DashboardTitle() { Title = HomeStrings.UserTitle; - Description = "view your friends and other information"; + Description = HeaderDescriptionStrings.Dashboard; IconTexture = "Icons/Hexacons/social"; } } From 49f0c707f6cb4e5676105f88e015966dbc41794c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Jul 2021 22:34:28 +0900 Subject: [PATCH 184/489] Move approach circle hiding within `BeginAbsoluteSequence` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5fa3bc4520..471847dbd6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -147,21 +147,22 @@ namespace osu.Game.Rulesets.Osu.Mods double startTime = circle.HitObject.StartTime; double preempt = circle.HitObject.TimePreempt; - using (drawable.BeginAbsoluteSequence(startTime - preempt)) + using (circle.BeginAbsoluteSequence(startTime - preempt)) { // initial state - drawable.ScaleTo(0.5f) + circle.ScaleTo(0.5f) .FadeColour(OsuColour.Gray(0.5f)); // scale to final size - drawable.ScaleTo(1f, preempt); + circle.ScaleTo(1f, preempt); + + // Remove approach circles + circle.ApproachCircle.Hide(); } - using (drawable.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) - drawable.FadeColour(Color4.White, undim_duration); + using (circle.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) + circle.FadeColour(Color4.White, undim_duration); - // Remove approach circles - circle.ApproachCircle.Hide(); } #endregion From fdebe4b94ac30050334dc6330308c54506a50079 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 18 Jul 2021 22:01:26 +0800 Subject: [PATCH 185/489] Code formatting fixes --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 471847dbd6..918b9b1c94 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Osu.Mods { // initial state circle.ScaleTo(0.5f) - .FadeColour(OsuColour.Gray(0.5f)); + .FadeColour(OsuColour.Gray(0.5f)); // scale to final size circle.ScaleTo(1f, preempt); @@ -162,7 +162,6 @@ namespace osu.Game.Rulesets.Osu.Mods using (circle.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) circle.FadeColour(Color4.White, undim_duration); - } #endregion From ee220feecf09781876973366ea136b22c6d5b809 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 18 Jul 2021 16:04:23 +0200 Subject: [PATCH 186/489] Avoid using guesses to determine whether inputs blocked --- .../Compose/Components/BlueprintContainer.cs | 12 ++++--- .../Timeline/TimelineBlueprintContainer.cs | 33 +++++-------------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 185f029d14..06cbf7c2e0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -110,9 +110,9 @@ namespace osu.Game.Screens.Edit.Compose.Components bool selectionPerformed = performMouseDownActions(e); // even if a selection didn't occur, a drag event may still move the selection. - prepareSelectionMovement(); + bool movementPossible = prepareSelectionMovement(); - return selectionPerformed || e.Button == MouseButton.Left; + return selectionPerformed || movementPossible; } protected SelectionBlueprint ClickedBlueprint { get; private set; } @@ -427,19 +427,21 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Attempts to begin the movement of any selected blueprints. /// - private void prepareSelectionMovement() + /// Whether a movement is possible. + private bool prepareSelectionMovement() { if (!SelectionHandler.SelectedBlueprints.Any()) - return; + return false; // Any selected blueprint that is hovered can begin the movement of the group, however only the first item (according to SortForMovement) is used for movement. // A special case is added for when a click selection occurred before the drag if (!clickSelectionBegan && !SelectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) - return; + return false; // Movement is tracked from the blueprint of the earliest item, since it only makes sense to distance snap from that item movementBlueprints = SortForMovement(SelectionHandler.SelectedBlueprints).ToArray(); movementBlueprintOriginalPositions = movementBlueprints.Select(m => m.ScreenSpaceSelectionPoint).ToArray(); + return true; } /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index abd7f5923c..ae33eaa355 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -35,15 +35,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable placement; private SelectionBlueprint placementBlueprint; - // We want children to be able to be clicked and dragged - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + private SelectableAreaBackground backgroundBox; - // This drawable itself should still check whether the mouse is over it - private bool shouldHandleInputAt(Vector2 screenSpacePos) - { - float localY = ToLocalSpace(screenSpacePos).Y; - return DrawRectangle.Top <= localY && DrawRectangle.Bottom >= localY; - } + // We want children to be able to be clicked and dragged, regardless of this drawable's size + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public TimelineBlueprintContainer(HitObjectComposer composer) : base(composer) @@ -58,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load() { - AddInternal(new SelectableAreaBackground + AddInternal(backgroundBox = new SelectableAreaBackground { Colour = Color4.Black, Depth = float.MaxValue, @@ -97,25 +92,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected override bool OnMouseDown(MouseDownEvent e) - { - int selectionCount = SelectedItems.Count; - - // We let BlueprintContainer attempt a HitObject selection - // If it fails, we'll pass it this input back to the timeline so it can be dragged - // We know it failed if the selection count is unchanged after the selection attempt - if (base.OnMouseDown(e) && selectionCount != SelectedItems.Count) - return true; - - return false; - } - protected override bool OnDragStart(DragStartEvent e) { - if (!shouldHandleInputAt(e.ScreenSpaceMouseDownPosition)) - return false; + // We should only allow BlueprintContainer to create a drag box if the mouse is within selection bounds + if (backgroundBox.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition)) + return base.OnDragStart(e); - return base.OnDragStart(e); + return false; } protected override void OnDrag(DragEvent e) From 2e2a2bdd99bda1065e7f79f3064d6dcf40cc3dba Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 18 Jul 2021 17:20:30 +0200 Subject: [PATCH 187/489] Allow moving timeline selection when mousedown event is outside of blueprint container --- .../Timeline/TimelineBlueprintContainer.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index ae33eaa355..8e0b39513a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable placement; private SelectionBlueprint placementBlueprint; - private SelectableAreaBackground backgroundBox; - // We want children to be able to be clicked and dragged, regardless of this drawable's size public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; @@ -53,7 +51,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load() { - AddInternal(backgroundBox = new SelectableAreaBackground + AddInternal(new SelectableAreaBackground { Colour = Color4.Black, Depth = float.MaxValue, @@ -92,15 +90,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected override bool OnDragStart(DragStartEvent e) - { - // We should only allow BlueprintContainer to create a drag box if the mouse is within selection bounds - if (backgroundBox.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition)) - return base.OnDragStart(e); - - return false; - } - protected override void OnDrag(DragEvent e) { handleScrollViaDrag(e); @@ -319,6 +308,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool HandleDrag(MouseButtonEvent e) { + // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. + if (DrawRectangle.Top > e.MouseDownPosition.Y || DrawRectangle.Bottom < e.MouseDownPosition.Y) + return false; + selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; // only calculate end when a transition is not in progress to avoid bouncing. From 1cc9e504cf2bc922a19253a007d12be80646380d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 02:17:21 +0900 Subject: [PATCH 188/489] Remove incorrectly added null coalesce --- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 4eac60f0eb..811e654387 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Wiki private void onCurrentChange(ValueChangedEvent e) { - if (e?.NewValue == TabControl.Items.LastOrDefault()) + if (e.NewValue == TabControl.Items.LastOrDefault()) return; if (e.NewValue == IndexPageString) From 9257cd7fad4c3883389d8620daffd78b1e18cee7 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 18 Jul 2021 19:18:06 +0200 Subject: [PATCH 189/489] Revert the use of an enum in `ProfileHeader`. --- osu.Game/Overlays/Profile/ProfileHeader.cs | 31 +++------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 083725418e..88c785d4e1 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -15,7 +14,7 @@ using osu.Game.Users; namespace osu.Game.Overlays.Profile { - public class ProfileHeader : TabControlOverlayHeader + public class ProfileHeader : TabControlOverlayHeader { private UserCoverBackground coverContainer; @@ -30,6 +29,9 @@ namespace osu.Game.Overlays.Profile User.ValueChanged += e => updateDisplay(e.NewValue); + TabControl.AddItem(LayoutStrings.HeaderUsersShow); + TabControl.AddItem(LayoutStrings.HeaderUsersModding); + centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } @@ -107,29 +109,4 @@ namespace osu.Game.Overlays.Profile protected override double LoadDelay => 0; } } - - [LocalisableEnum(typeof(ProfileHeaderTabEnumLocalisationMapper))] - public enum ProfileHeaderTab - { - Info, - Modding, - } - - public class ProfileHeaderTabEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(ProfileHeaderTab value) - { - switch (value) - { - case ProfileHeaderTab.Info: - return LayoutStrings.HeaderUsersShow; - - case ProfileHeaderTab.Modding: - return LayoutStrings.HeaderUsersModding; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } From 80885301a391e57d5d413337e87b1495da7624f1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 18 Jul 2021 19:36:34 +0200 Subject: [PATCH 190/489] Fix codefactor issues. --- osu.Game/Overlays/Profile/ProfileHeader.cs | 1 - .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 88c785d4e1..815f9fdafc 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -32,7 +32,6 @@ namespace osu.Game.Overlays.Profile TabControl.AddItem(LayoutStrings.HeaderUsersShow); TabControl.AddItem(LayoutStrings.HeaderUsersModding); - centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index af39251781..e6fd09301e 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -12,8 +12,8 @@ using osu.Framework.Allocation; using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; using osuTK; -using static osu.Game.Users.User; using osu.Framework.Localisation; +using static osu.Game.Users.User; namespace osu.Game.Overlays.Profile.Sections.Historical { From fb5d25405e82013fcc71bd06786dc68d94435662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 18 Jul 2021 21:52:16 +0200 Subject: [PATCH 191/489] Replace calls to obsoleted `GetOrDefault()` extension --- .../Difficulty/CatchPerformanceCalculator.cs | 11 +++++------ .../Difficulty/ManiaPerformanceCalculator.cs | 13 ++++++------- .../Difficulty/OsuPerformanceCalculator.cs | 9 ++++----- .../Difficulty/TaikoPerformanceCalculator.cs | 9 ++++----- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 11 +++++------ osu.Game/Scoring/ScoreInfo.cs | 7 +++---- osu.Game/Scoring/ScoreManager.cs | 3 +-- 7 files changed, 28 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index fdd6ac0857..439890dac2 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -33,11 +32,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty { mods = Score.Mods; - fruitsHit = Score.Statistics.GetOrDefault(HitResult.Great); - ticksHit = Score.Statistics.GetOrDefault(HitResult.LargeTickHit); - tinyTicksHit = Score.Statistics.GetOrDefault(HitResult.SmallTickHit); - tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss); - misses = Score.Statistics.GetOrDefault(HitResult.Miss); + fruitsHit = Score.Statistics.GetValueOrDefault(HitResult.Great); + ticksHit = Score.Statistics.GetValueOrDefault(HitResult.LargeTickHit); + tinyTicksHit = Score.Statistics.GetValueOrDefault(HitResult.SmallTickHit); + tinyTicksMissed = Score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss); + misses = Score.Statistics.GetValueOrDefault(HitResult.Miss); // We are heavily relying on aim in catch the beat double value = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0049) - 4.0, 2.0) / 100000.0; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 405ac56e94..b04ff3548f 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -37,12 +36,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty { mods = Score.Mods; scaledScore = Score.TotalScore; - countPerfect = Score.Statistics.GetOrDefault(HitResult.Perfect); - countGreat = Score.Statistics.GetOrDefault(HitResult.Great); - countGood = Score.Statistics.GetOrDefault(HitResult.Good); - countOk = Score.Statistics.GetOrDefault(HitResult.Ok); - countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); - countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); + countPerfect = Score.Statistics.GetValueOrDefault(HitResult.Perfect); + countGreat = Score.Statistics.GetValueOrDefault(HitResult.Great); + countGood = Score.Statistics.GetValueOrDefault(HitResult.Good); + countOk = Score.Statistics.GetValueOrDefault(HitResult.Ok); + countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); IEnumerable scoreIncreaseMods = Ruleset.GetModsFor(ModType.DifficultyIncrease); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 8a3c426381..a76db4abe3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; @@ -36,10 +35,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty mods = Score.Mods; accuracy = Score.Accuracy; scoreMaxCombo = Score.MaxCombo; - countGreat = Score.Statistics.GetOrDefault(HitResult.Great); - countOk = Score.Statistics.GetOrDefault(HitResult.Ok); - countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); - countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); + countGreat = Score.Statistics.GetValueOrDefault(HitResult.Great); + countOk = Score.Statistics.GetValueOrDefault(HitResult.Ok); + countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); // Custom multipliers for NoFail and SpunOut. double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 6117ed1673..90dd733dfd 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -31,10 +30,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public override double Calculate(Dictionary categoryDifficulty = null) { mods = Score.Mods; - countGreat = Score.Statistics.GetOrDefault(HitResult.Great); - countOk = Score.Statistics.GetOrDefault(HitResult.Ok); - countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); - countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); + countGreat = Score.Statistics.GetValueOrDefault(HitResult.Great); + countOk = Score.Statistics.GetValueOrDefault(HitResult.Ok); + countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); // Custom multipliers for NoFail and SpunOut. double multiplier = 1.1; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f32f70d4ba..eeb881cd39 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; @@ -146,7 +145,7 @@ namespace osu.Game.Rulesets.Scoring rollingMaxBaseScore += result.Judgement.MaxNumericResult; } - scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1; + scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1; hitEvents.Add(CreateHitEvent(result)); lastHitObject = result.HitObject; @@ -181,7 +180,7 @@ namespace osu.Game.Rulesets.Scoring rollingMaxBaseScore -= result.Judgement.MaxNumericResult; } - scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1; + scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1; Debug.Assert(hitEvents.Count > 0); lastHitObject = hitEvents[^1].LastHitObject; @@ -272,8 +271,8 @@ namespace osu.Game.Rulesets.Scoring private double calculateComboRatio(int maxCombo) => maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; private double getBonusScore(Dictionary statistics) - => statistics.GetOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE - + statistics.GetOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE; + => statistics.GetValueOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE + + statistics.GetValueOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE; private ScoreRank rankFrom(double acc) { @@ -291,7 +290,7 @@ namespace osu.Game.Rulesets.Scoring return ScoreRank.D; } - public int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); + public int GetStatistic(HitResult result) => scoreResultCounts.GetValueOrDefault(result); public double GetStandardisedScore() => getScore(ScoringMode.Standardised); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 4fd1d00fef..1d22dab0af 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; @@ -209,13 +208,13 @@ namespace osu.Game.Scoring { foreach (var r in Ruleset.CreateInstance().GetHitResults()) { - int value = Statistics.GetOrDefault(r.result); + int value = Statistics.GetValueOrDefault(r.result); switch (r.result) { case HitResult.SmallTickHit: { - int total = value + Statistics.GetOrDefault(HitResult.SmallTickMiss); + int total = value + Statistics.GetValueOrDefault(HitResult.SmallTickMiss); if (total > 0) yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); @@ -224,7 +223,7 @@ namespace osu.Game.Scoring case HitResult.LargeTickHit: { - int total = value + Statistics.GetOrDefault(HitResult.LargeTickMiss); + int total = value + Statistics.GetValueOrDefault(HitResult.LargeTickMiss); if (total > 0) yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index ebbdc8a109..83bcac01ac 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -210,7 +209,7 @@ namespace osu.Game.Scoring { // This is guaranteed to be a non-legacy score. // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values. - beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum(); + beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetValueOrDefault(r)).Sum(); } updateScore(beatmapMaxCombo, accuracy); From e0af5f0469d97a0b4b371cc0b4874d5aeed0294e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 11:38:28 +0900 Subject: [PATCH 192/489] Fix osu!(lazer) overwriting osu!(stable) desktop icons by adding back a suffix As discussed in https://github.com/ppy/osu/issues/13864, Squirrel will use the product name before the title, allowing us to use this variable to update the icon while not changing the window display title or naming elsewhere. --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 53a4e5edf5..89b9ffb94b 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -6,7 +6,7 @@ A free-to-win rhythm game. Rhythm is just a *click* away! osu! osu! - osu! + osu!(lazer) lazer.ico app.manifest 0.0.0 From d7f997a7f3f221279ee4b1497129655950adb525 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 12:36:13 +0900 Subject: [PATCH 193/489] Set score's rank on a failed submission As we don't have a `RankInfo.F`, this is the next best choice. I am also adding a check osu-web side for this - this is just to make sure we aren't sending scores with SS when they are not actually completed. I'm working on a separate PR to ensure this does not get mutated during the player exit process. --- osu.Game/Screens/Play/Player.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 97854ee12f..dc7ac1a48b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -953,7 +953,11 @@ namespace osu.Game.Screens.Play // if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap. if (prepareScoreForDisplayTask == null) + { Score.ScoreInfo.Passed = false; + // potentially should be ScoreRank.F instead? this is the best alternative for now. + Score.ScoreInfo.Rank = ScoreRank.D; + } // EndPlaying() is typically called from ReplayRecorder.Dispose(). Disposal is currently asynchronous. // To resolve test failures, forcefully end playing synchronously when this screen exits. From 3c028ce05cd619c58488793e9e26548ae07aeff3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 12:38:22 +0900 Subject: [PATCH 194/489] Add `IDeepCloneable` interface and update existing `CreateCopy` methods to use it --- CodeAnalysis/BannedSymbols.txt | 1 + osu.Game.Tests/NonVisual/ControlPointInfoTest.cs | 4 ++-- .../Visual/UserInterface/TestSceneModSettings.cs | 4 ++-- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 5 +++-- .../Beatmaps/ControlPoints/ControlPointInfo.cs | 7 ++++--- osu.Game/Online/Rooms/Room.cs | 5 +++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- .../Rulesets/Difficulty/DifficultyCalculator.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 4 ++-- osu.Game/Rulesets/Mods/MultiMod.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- .../Screens/OnlinePlay/OnlinePlaySongSelect.cs | 8 ++++---- .../OnlinePlay/Playlists/PlaylistsSongSelect.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Utils/IDeepCloneable.cs | 16 ++++++++++++++++ 16 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 osu.Game/Utils/IDeepCloneable.cs diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 46c50dbfa2..ea3e25142c 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -3,6 +3,7 @@ M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Us M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable or EqualityComparer.Default instead. M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. +T:SixLabors.ImageSharp.IDeepCloneable`1;Use osu.Game.Utils.IDeepCloneable instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900) T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index b27c257795..240ae4a90c 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -248,13 +248,13 @@ namespace osu.Game.Tests.NonVisual } [Test] - public void TestCreateCopyIsDeepClone() + public void TestDeepClone() { var cpi = new ControlPointInfo(); cpi.Add(1000, new TimingControlPoint { BeatLength = 500 }); - var cpiCopy = cpi.CreateCopy(); + var cpiCopy = cpi.DeepClone(); cpiCopy.Add(2000, new TimingControlPoint { BeatLength = 500 }); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index bda1973354..65db2e9644 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create mods", () => { original = new OsuModDoubleTime(); - copy = (OsuModDoubleTime)original.CreateCopy(); + copy = (OsuModDoubleTime)original.DeepClone(); }); AddStep("change property", () => original.SpeedChange.Value = 2); @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create mods", () => { original = new MultiMod(new OsuModDoubleTime()); - copy = (MultiMod)original.CreateCopy(); + copy = (MultiMod)original.DeepClone(); }); AddStep("change property", () => ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2); diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index e8dc623ddb..643c5d9adb 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -3,11 +3,12 @@ using System; using osu.Game.Graphics; +using osu.Game.Utils; using osuTK.Graphics; namespace osu.Game.Beatmaps.ControlPoints { - public abstract class ControlPoint : IComparable + public abstract class ControlPoint : IComparable, IDeepCloneable { /// /// The time at which the control point takes effect. @@ -32,7 +33,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// Create an unbound copy of this control point. /// - public ControlPoint CreateCopy() + public ControlPoint DeepClone() { var copy = (ControlPoint)Activator.CreateInstance(GetType()); diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 25d0843a71..2d0fc17a7b 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -10,11 +10,12 @@ using osu.Framework.Bindables; using osu.Framework.Lists; using osu.Framework.Utils; using osu.Game.Screens.Edit; +using osu.Game.Utils; namespace osu.Game.Beatmaps.ControlPoints { [Serializable] - public class ControlPointInfo + public class ControlPointInfo : IDeepCloneable { /// /// All control points grouped by time. @@ -350,12 +351,12 @@ namespace osu.Game.Beatmaps.ControlPoints } } - public ControlPointInfo CreateCopy() + public ControlPointInfo DeepClone() { var controlPointInfo = new ControlPointInfo(); foreach (var point in AllControlPoints) - controlPointInfo.Add(point.Time, point.CreateCopy()); + controlPointInfo.Add(point.Time, point.DeepClone()); return controlPointInfo; } diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b28680ffef..863fefd55c 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -10,10 +10,11 @@ using osu.Game.IO.Serialization.Converters; using osu.Game.Online.Rooms.GameTypes; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Users; +using osu.Game.Utils; namespace osu.Game.Online.Rooms { - public class Room + public class Room : IDeepCloneable { [Cached] [JsonProperty("id")] @@ -120,7 +121,7 @@ namespace osu.Game.Online.Rooms /// Create a copy of this room without online information. /// Should be used to create a local copy of a room for submitting in the future. /// - public Room CreateCopy() + public Room DeepClone() { var copy = new Room(); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 98a79a62c8..793bb79318 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -429,7 +429,7 @@ namespace osu.Game.Overlays.Mods if (!Stacked) modEnumeration = ModUtils.FlattenMods(modEnumeration); - section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null).Select(m => m.CreateCopy()); + section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null).Select(m => m.DeepClone()); } updateSelectedButtons(); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 3cc69bd85b..224c9178ae 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Difficulty /// A structure describing the difficulty of the beatmap. public DifficultyAttributes Calculate(params Mod[] mods) { - mods = mods.Select(m => m.CreateCopy()).ToArray(); + mods = mods.Select(m => m.DeepClone()).ToArray(); IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 6f00bb6c75..f2fd02c652 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods /// The base class for gameplay modifiers. /// [ExcludeFromDynamicCompile] - public abstract class Mod : IMod, IEquatable, IJsonSerializable + public abstract class Mod : IMod, IEquatable, IJsonSerializable, IDeepCloneable { /// /// The name of this mod. @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Mods /// /// Creates a copy of this initialised to a default state. /// - public virtual Mod CreateCopy() + public virtual Mod DeepClone() { var result = (Mod)Activator.CreateInstance(GetType()); result.CopyFrom(this); diff --git a/osu.Game/Rulesets/Mods/MultiMod.cs b/osu.Game/Rulesets/Mods/MultiMod.cs index 2107009dbb..1c41c6b8b3 100644 --- a/osu.Game/Rulesets/Mods/MultiMod.cs +++ b/osu.Game/Rulesets/Mods/MultiMod.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods Mods = mods; } - public override Mod CreateCopy() => new MultiMod(Mods.Select(m => m.CreateCopy()).ToArray()); + public override Mod DeepClone() => new MultiMod(Mods.Select(m => m.DeepClone()).ToArray()); public override Type[] IncompatibleMods => Mods.SelectMany(m => m.IncompatibleMods).ToArray(); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 986a4efb28..71dd47b058 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -128,7 +128,7 @@ namespace osu.Game.Screens.Edit // clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages. // eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases. - playableBeatmap.ControlPointInfo = playableBeatmap.ControlPointInfo.CreateCopy(); + playableBeatmap.ControlPointInfo = playableBeatmap.ControlPointInfo.DeepClone(); } catch (Exception e) { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 35782c6104..b4b1a09c4f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -242,7 +242,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new OsuMenuItem("Create copy", MenuItemType.Standard, () => { - parentScreen?.OpenNewRoom(Room.CreateCopy()); + parentScreen?.OpenNewRoom(Room.DeepClone()); }) }; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 2c46f76737..be28de5c43 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -72,8 +72,8 @@ namespace osu.Game.Screens.OnlinePlay // At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods. // Similarly, freeMods is currently empty but should only contain the allowed mods. - Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); - FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); + Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty(); + FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty(); Mods.BindValueChanged(onModsChanged); Ruleset.BindValueChanged(onRulesetChanged); @@ -108,8 +108,8 @@ namespace osu.Game.Screens.OnlinePlay } }; - item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); - item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy())); + item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone())); + item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone())); SelectItem(item); return true; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs index 21335fc90c..076fa77336 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs @@ -55,10 +55,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists item.Ruleset.Value = Ruleset.Value; item.RequiredMods.Clear(); - item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); + item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone())); item.AllowedMods.Clear(); - item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy())); + item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone())); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 97854ee12f..51f1dbd121 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game) { - Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray(); + Mods.Value = base.Mods.Value.Select(m => m.DeepClone()).ToArray(); if (Beatmap.Value is DummyWorkingBeatmap) return; diff --git a/osu.Game/Utils/IDeepCloneable.cs b/osu.Game/Utils/IDeepCloneable.cs new file mode 100644 index 0000000000..6877f346c4 --- /dev/null +++ b/osu.Game/Utils/IDeepCloneable.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Utils +{ + /// A generic interface for a deeply cloneable type. + /// The type of object to clone. + public interface IDeepCloneable where T : class + { + /// + /// Creates a new that is a deep copy of the current instance. + /// + /// The . + T DeepClone(); + } +} From 70c9d7105feb5162661379f427562de6cba17f91 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:33:22 +0900 Subject: [PATCH 195/489] Add a function to compute hit object position in catch editor --- .../Edit/CatchHitObjectUtils.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs new file mode 100644 index 0000000000..beffdf0362 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit +{ + /// + /// Utility functions used by the editor. + /// + public static class CatchHitObjectUtils + { + /// + /// Get the position of the hit object in the playfield based on and . + /// + public static Vector2 GetStartPosition(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject) + { + return new Vector2(hitObject.OriginalX, hitObjectContainer.PositionAtTime(hitObject.StartTime)); + } + } +} From c34758485125a3d0ad2f41d28c4c60e1771b2fc1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:33:46 +0900 Subject: [PATCH 196/489] Use added utility function --- .../Edit/Blueprints/CatchSelectionBlueprint.cs | 5 ++--- .../Edit/Blueprints/Components/FruitOutline.cs | 6 +----- .../Edit/Blueprints/Components/NestedOutlineContainer.cs | 9 ++------- .../Edit/Blueprints/Components/ScrollingPath.cs | 6 ------ .../Edit/Blueprints/FruitPlacementBlueprint.cs | 3 ++- .../Edit/Blueprints/FruitSelectionBlueprint.cs | 6 ++++-- .../Edit/Blueprints/JuiceStreamSelectionBlueprint.cs | 3 +-- 7 files changed, 12 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs index 720d730858..7e566c810c 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs @@ -19,9 +19,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { get { - float x = HitObject.OriginalX; - float y = HitObjectContainer.PositionAtTime(HitObject.StartTime); - return HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight)); + Vector2 position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + return HitObjectContainer.ToScreenSpace(position + new Vector2(0, HitObjectContainer.DrawHeight)); } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs index 345b59bdcd..0c03068e26 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs @@ -1,14 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Skinning.Default; -using osu.Game.Rulesets.UI.Scrolling; using osuTK; namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components @@ -28,10 +26,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components Colour = osuColour.Yellow; } - public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject, [CanBeNull] CatchHitObject parent = null) + public void UpdateFrom(CatchHitObject hitObject) { - X = hitObject.EffectiveX - (parent?.OriginalX ?? 0); - Y = hitObjectContainer.PositionAtTime(hitObject.StartTime, parent?.StartTime ?? hitObjectContainer.Time.Current); Scale = new Vector2(hitObject.Scale); } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs index 48d90e8b24..cf916b27a4 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs @@ -20,12 +20,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components Anchor = Anchor.BottomLeft; } - public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject) - { - X = parentHitObject.OriginalX; - Y = hitObjectContainer.PositionAtTime(parentHitObject.StartTime); - } - public void UpdateNestedObjectsFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject) { nestedHitObjects.Clear(); @@ -43,7 +37,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { var hitObject = nestedHitObjects[i]; var outline = (FruitOutline)InternalChildren[i]; - outline.UpdateFrom(hitObjectContainer, hitObject, parentHitObject); + outline.Position = CatchHitObjectUtils.GetStartPosition(hitObjectContainer, hitObject) - Position; + outline.UpdateFrom(hitObject); outline.Scale *= hitObject is Droplet ? 0.5f : 1; } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs index 96111beda4..109bf61ea5 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs @@ -33,12 +33,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components }; } - public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject) - { - X = hitObject.OriginalX; - Y = hitObjectContainer.PositionAtTime(hitObject.StartTime); - } - public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject) { double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity); diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs index 0f28cf6786..e169e3b75c 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs @@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { base.Update(); - outline.UpdateFrom(HitObjectContainer, HitObject); + outline.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + outline.UpdateFrom(HitObject); } protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs index 9665aac2fb..150297badb 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs @@ -20,8 +20,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { base.Update(); - if (IsSelected) - outline.UpdateFrom(HitObjectContainer, HitObject); + if (!IsSelected) return; + + outline.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + outline.UpdateFrom(HitObject); } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index bf7b962e0a..0614c4c24d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -49,8 +49,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints if (!IsSelected) return; - scrollingPath.UpdatePositionFrom(HitObjectContainer, HitObject); - nestedOutlineContainer.UpdatePositionFrom(HitObjectContainer, HitObject); + nestedOutlineContainer.Position = scrollingPath.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); if (pathCache.IsValid) return; From be495a74887f12011df92911f1a72ff6d8b1b59e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:03:53 +0900 Subject: [PATCH 197/489] Add `JuiceStreamPath` to allow editing `JuiceStream` in catch editor A `JuiceStream` holds a legacy `SliderPath` as the representation of the path. However, the `SliderPath` representation is difficult to work with because `SliderPath` works in 2D position, while osu!catch works in `(time, x)` coordinates. This `JuiceStreamPath` represents the path in a more convenient way, a polyline connecting list of `(distance, x)` vertices. --- .../Objects/JuiceStreamPath.cs | 340 ++++++++++++++++++ .../Objects/JuiceStreamPathVertex.cs | 33 ++ 2 files changed, 373 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs new file mode 100644 index 0000000000..ac11bd9918 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -0,0 +1,340 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Utils; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Objects +{ + /// + /// Represents the path of a juice stream. + /// + /// A holds a legacy as the representation of the path. + /// However, the representation is difficult to work with. + /// This represents the path in a more convenient way, a polyline connecting list of s. + /// + /// + /// The path can be regarded as a function from the closed interval [Vertices[0].Distance, Vertices[^1].Distance] to the x position, given by . + /// To ensure the path is convertible to a , the slope of the function must not be more than 1 everywhere, + /// and this slope condition is always maintained as an invariant. + /// + /// + public class JuiceStreamPath + { + /// + /// The list of vertices of the path, which is represented as a polyline connecting the vertices. + /// + public IReadOnlyList Vertices => vertices; + + /// + /// The current version number. + /// This starts from 1 and incremented whenever this is modified. + /// + public int InvalidationID { get; private set; } = 1; + + /// + /// The difference between first vertex's and last vertex's . + /// + public double Distance => vertices[^1].Distance - vertices[0].Distance; + + /// + /// This list should always be non-empty. + /// + private readonly List vertices = new List + { + new JuiceStreamPathVertex() + }; + + /// + /// Compute the x-position of the path at the given . + /// + /// + /// When the given distance is outside of the path, the x position at the corresponding endpoint is returned, + /// + public float PositionAtDistance(double distance) + { + int index = vertexIndexAtDistance(distance); + return positionAtDistance(distance, index); + } + + /// + /// Remove all vertices of this path, then add a new vertex (0, 0). + /// + public void Clear() + { + vertices.Clear(); + vertices.Add(new JuiceStreamPathVertex()); + invalidate(); + } + + /// + /// Insert a vertex at given . + /// The is used as the position of the new vertex. + /// Thus, the set of points of the path is not changed (up to floating-point precision). + /// + /// The index of the new vertex. + public int InsertVertex(double distance) + { + if (!double.IsFinite(distance)) + throw new ArgumentOutOfRangeException(nameof(distance)); + + int index = vertexIndexAtDistance(distance); + float x = positionAtDistance(distance, index); + vertices.Insert(index, new JuiceStreamPathVertex(distance, x)); + + invalidate(); + return index; + } + + /// + /// Move the vertex of given to the given position . + /// When the distances between vertices are too small for the new vertex positions, the adjacent vertices are moved towards . + /// + public void SetVertexPosition(int index, float newX) + { + if (index < 0 || index >= vertices.Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + if (!float.IsFinite(newX)) + throw new ArgumentOutOfRangeException(nameof(newX)); + + var newVertex = new JuiceStreamPathVertex(vertices[index].Distance, newX); + + for (int i = index - 1; i >= 0 && !canConnect(vertices[i], newVertex); i--) + { + float clampedX = clampToConnectablePosition(newVertex, vertices[i]); + vertices[i] = new JuiceStreamPathVertex(vertices[i].Distance, clampedX); + } + + for (int i = index + 1; i < vertices.Count; i++) + { + float clampedX = clampToConnectablePosition(newVertex, vertices[i]); + vertices[i] = new JuiceStreamPathVertex(vertices[i].Distance, clampedX); + } + + vertices[index] = newVertex; + + invalidate(); + } + + /// + /// Add a new vertex at given and position. + /// Adjacent vertices are moved when necessary in the same way as . + /// + public void Add(double distance, float x) + { + int index = InsertVertex(distance); + SetVertexPosition(index, x); + } + + /// + /// Remove all vertices that satisfy the given . + /// + /// + /// If all vertices are removed, a new vertex (0, 0) is added. + /// + /// The predicate to determine whether a vertex should be removed given the vertex and its index in the path. + /// The number of removed vertices. + public int RemoveVertices(Func predicate) + { + int index = 0; + int removeCount = vertices.RemoveAll(vertex => predicate(vertex, index++)); + + if (vertices.Count == 0) + vertices.Add(new JuiceStreamPathVertex()); + + if (removeCount != 0) + invalidate(); + + return removeCount; + } + + /// + /// Recreate this path by using difference set of vertices at given distances. + /// In addition to the given , the first vertex and the last vertex are always added to the new path. + /// New vertices use the positions on the original path. Thus, s at are preserved. + /// + public void ResampleVertices(IEnumerable sampleDistances) + { + var sampledVertices = new List(); + + foreach (double distance in sampleDistances) + { + if (!double.IsFinite(distance)) + throw new ArgumentOutOfRangeException(nameof(sampleDistances)); + + double clampedDistance = Math.Clamp(distance, vertices[0].Distance, vertices[^1].Distance); + float x = PositionAtDistance(clampedDistance); + sampledVertices.Add(new JuiceStreamPathVertex(clampedDistance, x)); + } + + sampledVertices.Sort(); + + // The first vertex and the last vertex are always used in the result. + vertices.RemoveRange(1, vertices.Count - (vertices.Count == 1 ? 1 : 2)); + vertices.InsertRange(1, sampledVertices); + + invalidate(); + } + + /// + /// Convert a to list of vertices and write the result to this . + /// + /// + /// Duplicated vertices are automatically removed. + /// + public void ConvertFromSliderPath(SliderPath sliderPath) + { + var sliderPathVertices = new List(); + sliderPath.GetPathToProgress(sliderPathVertices, 0, 1); + + double distance = 0; + + vertices.Clear(); + vertices.Add(new JuiceStreamPathVertex(0, sliderPathVertices.FirstOrDefault().X)); + + for (int i = 1; i < sliderPathVertices.Count; i++) + { + distance += Vector2.Distance(sliderPathVertices[i - 1], sliderPathVertices[i]); + + if (!Precision.AlmostEquals(vertices[^1].Distance, distance)) + vertices.Add(new JuiceStreamPathVertex(distance, sliderPathVertices[i].X)); + } + + invalidate(); + } + + /// + /// The height of legacy osu!standard playfield. + /// The sliders converted by are vertically contained in this height. + /// + public const float OSU_PLAYFIELD_HEIGHT = 384; + + /// + /// Convert the path of this to a and write the result to . + /// The resulting slider is "folded" to make it vertically contained in the playfield `(0..)` assuming the slider start position is . + /// + public void ConvertToSliderPath(SliderPath sliderPath, float sliderStartY) + { + const float margin = 1; + + // Note: these two variables and `sliderPath` are modified by the local functions. + double currentDistance = 0; + Vector2 lastPosition = new Vector2(vertices[0].X, 0); + + sliderPath.ControlPoints.Clear(); + sliderPath.ControlPoints.Add(new PathControlPoint(lastPosition)); + + for (int i = 1; i < vertices.Count; i++) + { + sliderPath.ControlPoints[^1].Type.Value = PathType.Linear; + + float deltaX = vertices[i].X - lastPosition.X; + double length = vertices[i].Distance - currentDistance; + + // Should satisfy `deltaX^2 + deltaY^2 = length^2`. + // By invariants, the expression inside the `sqrt` is (almost) non-negative. + double deltaY = Math.Sqrt(Math.Max(0, length * length - (double)deltaX * deltaX)); + + // When `deltaY` is small, one segment is always enough. + // This case is handled separately to prevent divide-by-zero. + if (deltaY <= OSU_PLAYFIELD_HEIGHT / 2 - margin) + { + float nextX = vertices[i].X; + float nextY = (float)(lastPosition.Y + getYDirection() * deltaY); + addControlPoint(nextX, nextY); + continue; + } + + // When `deltaY` is large or when the slider velocity is fast, the segment must be partitioned to subsegments to stay in bounds. + for (double currentProgress = 0; currentProgress < deltaY;) + { + double nextProgress = Math.Min(currentProgress + getMaxDeltaY(), deltaY); + float nextX = (float)(vertices[i - 1].X + nextProgress / deltaY * deltaX); + float nextY = (float)(lastPosition.Y + getYDirection() * (nextProgress - currentProgress)); + addControlPoint(nextX, nextY); + currentProgress = nextProgress; + } + } + + int getYDirection() + { + float lastSliderY = sliderStartY + lastPosition.Y; + return lastSliderY < OSU_PLAYFIELD_HEIGHT / 2 ? 1 : -1; + } + + float getMaxDeltaY() + { + float lastSliderY = sliderStartY + lastPosition.Y; + return Math.Max(lastSliderY, OSU_PLAYFIELD_HEIGHT - lastSliderY) - margin; + } + + void addControlPoint(float nextX, float nextY) + { + Vector2 nextPosition = new Vector2(nextX, nextY); + sliderPath.ControlPoints.Add(new PathControlPoint(nextPosition)); + currentDistance += Vector2.Distance(lastPosition, nextPosition); + lastPosition = nextPosition; + } + } + + /// + /// Find the index at which a new vertex with can be inserted. + /// + private int vertexIndexAtDistance(double distance) + { + // The position of `(distance, Infinity)` is uniquely determined because infinite positions are not allowed. + int i = vertices.BinarySearch(new JuiceStreamPathVertex(distance, float.PositiveInfinity)); + return i < 0 ? ~i : i; + } + + /// + /// Compute the position at the given , assuming is the vertex index returned by . + /// + private float positionAtDistance(double distance, int index) + { + if (index <= 0) + return vertices[0].X; + if (index >= vertices.Count) + return vertices[^1].X; + + double length = vertices[index].Distance - vertices[index - 1].Distance; + if (Precision.AlmostEquals(length, 0)) + return vertices[index].X; + + float deltaX = vertices[index].X - vertices[index - 1].X; + + return (float)(vertices[index - 1].X + deltaX * ((distance - vertices[index - 1].Distance) / length)); + } + + /// + /// Check the two vertices can connected directly while satisfying the slope condition. + /// + private bool canConnect(JuiceStreamPathVertex vertex1, JuiceStreamPathVertex vertex2, float allowance = 0) + { + double xDistance = Math.Abs((double)vertex2.X - vertex1.X); + float length = (float)Math.Abs(vertex2.Distance - vertex1.Distance); + return xDistance <= length + allowance; + } + + /// + /// Move the position of towards the position of + /// until the vertex pair satisfies the condition . + /// + /// The resulting position of . + private float clampToConnectablePosition(JuiceStreamPathVertex fixedVertex, JuiceStreamPathVertex movableVertex) + { + float length = (float)Math.Abs(movableVertex.Distance - fixedVertex.Distance); + return Math.Clamp(movableVertex.X, fixedVertex.X - length, fixedVertex.X + length); + } + + private void invalidate() => InvalidationID++; + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs new file mode 100644 index 0000000000..58c50603c4 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Objects +{ + /// + /// A vertex of a . + /// + public readonly struct JuiceStreamPathVertex : IComparable + { + public readonly double Distance; + + public readonly float X; + + public JuiceStreamPathVertex(double distance, float x) + { + Distance = distance; + X = x; + } + + public int CompareTo(JuiceStreamPathVertex other) + { + int c = Distance.CompareTo(other.Distance); + return c != 0 ? c : X.CompareTo(other.X); + } + + public override string ToString() => $"({Distance}, {X})"; + } +} From fffe0d2e5735d68c7beef525b5c134fe5822ba69 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:09:33 +0900 Subject: [PATCH 198/489] Add tests of `JuiceStreamPath`. "Property testing" is heavily used, tests that generates random cases and asserting properties. That gives high confidence of round-trip correctness of `SliderPath` conversion, for example. --- .../JuiceStreamPathTest.cs | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs new file mode 100644 index 0000000000..5e4b6d9e1a --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -0,0 +1,288 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class JuiceStreamPathTest + { + [TestCase(1e3, true, false)] + // When the coordinates are large, the slope invariant fails within the specified absolute allowance due to the floating-number precision. + [TestCase(1e9, false, false)] + // Using discrete values sometimes discover more edge cases. + [TestCase(10, true, true)] + public void TestRandomInsertSetPosition(double scale, bool checkSlope, bool integralValues) + { + var rng = new Random(1); + var path = new JuiceStreamPath(); + + for (int iteration = 0; iteration < 100000; iteration++) + { + if (rng.Next(10) == 0) + path.Clear(); + + int vertexCount = path.Vertices.Count; + + switch (rng.Next(2)) + { + case 0: + { + double distance = rng.NextDouble() * scale * 2 - scale; + if (integralValues) + distance = Math.Round(distance); + + float oldX = path.PositionAtDistance(distance); + int index = path.InsertVertex(distance); + Assert.That(path.Vertices.Count, Is.EqualTo(vertexCount + 1)); + Assert.That(path.Vertices[index].Distance, Is.EqualTo(distance)); + Assert.That(path.Vertices[index].X, Is.EqualTo(oldX)); + break; + } + + case 1: + { + int index = rng.Next(path.Vertices.Count); + double distance = path.Vertices[index].Distance; + float newX = (float)(rng.NextDouble() * scale * 2 - scale); + if (integralValues) + newX = MathF.Round(newX); + + path.SetVertexPosition(index, newX); + Assert.That(path.Vertices.Count, Is.EqualTo(vertexCount)); + Assert.That(path.Vertices[index].Distance, Is.EqualTo(distance)); + Assert.That(path.Vertices[index].X, Is.EqualTo(newX)); + break; + } + } + + assertInvariants(path.Vertices, checkSlope); + } + } + + [Test] + public void TestRemoveVertices() + { + var path = new JuiceStreamPath(); + path.Add(10, 5); + path.Add(20, -5); + + int removeCount = path.RemoveVertices((v, i) => v.Distance == 10 && i == 1); + Assert.That(removeCount, Is.EqualTo(1)); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(0, 0), + new JuiceStreamPathVertex(20, -5) + })); + + removeCount = path.RemoveVertices((_, i) => i == 0); + Assert.That(removeCount, Is.EqualTo(1)); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(20, -5) + })); + + removeCount = path.RemoveVertices((_, i) => true); + Assert.That(removeCount, Is.EqualTo(1)); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex() + })); + } + + [Test] + public void TestResampleVertices() + { + var path = new JuiceStreamPath(); + path.Add(-100, -10); + path.Add(100, 50); + path.ResampleVertices(new double[] + { + -50, + 0, + 70, + 120 + }); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(-100, -10), + new JuiceStreamPathVertex(-50, -5), + new JuiceStreamPathVertex(0, 0), + new JuiceStreamPathVertex(70, 35), + new JuiceStreamPathVertex(100, 50), + new JuiceStreamPathVertex(100, 50), + })); + + path.Clear(); + path.SetVertexPosition(0, 10); + path.ResampleVertices(Array.Empty()); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(0, 10) + })); + } + + [Test] + public void TestRandomConvertFromSliderPath() + { + var rng = new Random(1); + var path = new JuiceStreamPath(); + var sliderPath = new SliderPath(); + + for (int iteration = 0; iteration < 10000; iteration++) + { + sliderPath.ControlPoints.Clear(); + + do + { + int start = sliderPath.ControlPoints.Count; + + do + { + float x = (float)(rng.NextDouble() * 1e3); + float y = (float)(rng.NextDouble() * 1e3); + sliderPath.ControlPoints.Add(new PathControlPoint(new Vector2(x, y))); + } while (rng.Next(2) != 0); + + int length = sliderPath.ControlPoints.Count - start + 1; + sliderPath.ControlPoints[start].Type.Value = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; + } while (rng.Next(3) != 0); + + if (rng.Next(5) == 0) + sliderPath.ExpectedDistance.Value = rng.NextDouble() * 3e3; + else + sliderPath.ExpectedDistance.Value = null; + + path.ConvertFromSliderPath(sliderPath); + Assert.That(path.Vertices[0].Distance, Is.EqualTo(0)); + Assert.That(path.Distance, Is.EqualTo(sliderPath.Distance).Within(1e-3)); + assertInvariants(path.Vertices, true); + + double[] sampleDistances = Enumerable.Range(0, 10) + .Select(_ => rng.NextDouble() * sliderPath.Distance) + .ToArray(); + + foreach (double distance in sampleDistances) + { + float expected = sliderPath.PositionAt(distance / sliderPath.Distance).X; + Assert.That(path.PositionAtDistance(distance), Is.EqualTo(expected).Within(1e-3)); + } + + path.ResampleVertices(sampleDistances); + assertInvariants(path.Vertices, true); + + foreach (double distance in sampleDistances) + { + float expected = sliderPath.PositionAt(distance / sliderPath.Distance).X; + Assert.That(path.PositionAtDistance(distance), Is.EqualTo(expected).Within(1e-3)); + } + } + } + + [Test] + public void TestRandomConvertToSliderPath() + { + var rng = new Random(1); + var path = new JuiceStreamPath(); + var sliderPath = new SliderPath(); + + for (int iteration = 0; iteration < 10000; iteration++) + { + path.Clear(); + + do + { + double distance = rng.NextDouble() * 1e3; + float x = (float)(rng.NextDouble() * 1e3); + path.Add(distance, x); + } while (rng.Next(5) != 0); + + float sliderStartY = (float)(rng.NextDouble() * JuiceStreamPath.OSU_PLAYFIELD_HEIGHT); + + path.ConvertToSliderPath(sliderPath, sliderStartY); + Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3)); + Assert.That(sliderPath.ControlPoints[0].Position.Value.X, Is.EqualTo(path.Vertices[0].X)); + assertInvariants(path.Vertices, true); + + foreach (var point in sliderPath.ControlPoints) + { + Assert.That(point.Type.Value, Is.EqualTo(PathType.Linear).Or.Null); + Assert.That(sliderStartY + point.Position.Value.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); + } + + for (int i = 0; i < 10; i++) + { + double distance = rng.NextDouble() * path.Distance; + float expected = path.PositionAtDistance(distance); + Assert.That(sliderPath.PositionAt(distance / sliderPath.Distance).X, Is.EqualTo(expected).Within(1e-3)); + } + } + } + + [Test] + public void TestInvalidation() + { + var path = new JuiceStreamPath(); + Assert.That(path.InvalidationID, Is.EqualTo(1)); + int previousId = path.InvalidationID; + + path.InsertVertex(10); + checkNewId(); + + path.SetVertexPosition(1, 5); + checkNewId(); + + path.Add(20, 0); + checkNewId(); + + path.RemoveVertices((v, _) => v.Distance == 20); + checkNewId(); + + path.ResampleVertices(new double[] { 5, 10, 15 }); + checkNewId(); + + path.Clear(); + checkNewId(); + + path.ConvertFromSliderPath(new SliderPath()); + checkNewId(); + + void checkNewId() + { + Assert.That(path.InvalidationID, Is.Not.EqualTo(previousId)); + previousId = path.InvalidationID; + } + } + + private void assertInvariants(IReadOnlyList vertices, bool checkSlope) + { + Assert.That(vertices, Is.Not.Empty); + + for (int i = 0; i < vertices.Count; i++) + { + Assert.That(double.IsFinite(vertices[i].Distance)); + Assert.That(float.IsFinite(vertices[i].X)); + } + + for (int i = 1; i < vertices.Count; i++) + { + Assert.That(vertices[i].Distance, Is.GreaterThanOrEqualTo(vertices[i - 1].Distance)); + + if (!checkSlope) continue; + + float xDiff = Math.Abs(vertices[i].X - vertices[i - 1].X); + double distanceDiff = vertices[i].Distance - vertices[i - 1].Distance; + Assert.That(xDiff, Is.LessThanOrEqualTo(distanceDiff).Within(Precision.FLOAT_EPSILON)); + } + } + } +} From e507faef294748c4aac5686aef06fa343e83069b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 13:02:40 +0900 Subject: [PATCH 199/489] Add deep cloning support to `Score`/`ScoreInfo`/`Replay` --- osu.Game.Tests/NonVisual/ScoreInfoTest.cs | 33 +++++++++++++++++++++++ osu.Game/Replays/Replay.cs | 14 +++++++++- osu.Game/Scoring/Score.cs | 12 ++++++++- osu.Game/Scoring/ScoreInfo.cs | 11 +++++++- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/NonVisual/ScoreInfoTest.cs diff --git a/osu.Game.Tests/NonVisual/ScoreInfoTest.cs b/osu.Game.Tests/NonVisual/ScoreInfoTest.cs new file mode 100644 index 0000000000..6e5718cd4c --- /dev/null +++ b/osu.Game.Tests/NonVisual/ScoreInfoTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class ScoreInfoTest + { + [Test] + public void TestDeepClone() + { + var score = new ScoreInfo(); + + score.Statistics.Add(HitResult.Good, 10); + score.Rank = ScoreRank.B; + + var scoreCopy = score.DeepClone(); + + score.Statistics[HitResult.Good]++; + score.Rank = ScoreRank.X; + + Assert.That(scoreCopy.Statistics[HitResult.Good], Is.EqualTo(10)); + Assert.That(score.Statistics[HitResult.Good], Is.EqualTo(11)); + + Assert.That(scoreCopy.Rank, Is.EqualTo(ScoreRank.B)); + Assert.That(score.Rank, Is.EqualTo(ScoreRank.X)); + } + } +} diff --git a/osu.Game/Replays/Replay.cs b/osu.Game/Replays/Replay.cs index 5430915394..30e176b5c7 100644 --- a/osu.Game/Replays/Replay.cs +++ b/osu.Game/Replays/Replay.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Game.Rulesets.Replays; +using osu.Game.Utils; namespace osu.Game.Replays { - public class Replay + public class Replay : IDeepCloneable { /// /// Whether all frames for this replay have been received. @@ -15,5 +17,15 @@ namespace osu.Game.Replays public bool HasReceivedAllFrames = true; public List Frames = new List(); + + public Replay DeepClone() + { + return new Replay + { + HasReceivedAllFrames = HasReceivedAllFrames, + // individual frames are mutable for now but hopefully this will not be a thing in the future. + Frames = Frames.ToList(), + }; + } } } diff --git a/osu.Game/Scoring/Score.cs b/osu.Game/Scoring/Score.cs index 4e82b1584e..83e4389dc8 100644 --- a/osu.Game/Scoring/Score.cs +++ b/osu.Game/Scoring/Score.cs @@ -2,12 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Replays; +using osu.Game.Utils; namespace osu.Game.Scoring { - public class Score + public class Score : IDeepCloneable { public ScoreInfo ScoreInfo = new ScoreInfo(); public Replay Replay = new Replay(); + + public Score DeepClone() + { + return new Score + { + ScoreInfo = ScoreInfo.DeepClone(), + Replay = Replay.DeepClone(), + }; + } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1d22dab0af..a0c4d5a026 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -18,7 +18,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable + public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable { public int ID { get; set; } @@ -242,6 +242,15 @@ namespace osu.Game.Scoring } } + public ScoreInfo DeepClone() + { + var clone = (ScoreInfo)MemberwiseClone(); + + clone.Statistics = new Dictionary(clone.Statistics); + + return clone; + } + public override string ToString() => $"{User} playing {Beatmap}"; public bool Equals(ScoreInfo other) From caba78cb5d0ebd67053537e634d613fae733933c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 13:04:16 +0900 Subject: [PATCH 200/489] Copy score during submission process to ensure it isn't modified --- osu.Game/Screens/Play/SoloPlayer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index d90e8e0168..7d15fcf49f 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -38,13 +38,15 @@ namespace osu.Game.Screens.Play protected override APIRequest CreateSubmissionRequest(Score score, long token) { - var beatmap = score.ScoreInfo.Beatmap; + var scoreCopy = score.DeepClone(); + + var beatmap = scoreCopy.ScoreInfo.Beatmap; Debug.Assert(beatmap.OnlineBeatmapID != null); int beatmapId = beatmap.OnlineBeatmapID.Value; - return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); + return new SubmitSoloScoreRequest(beatmapId, token, scoreCopy.ScoreInfo); } } } From 87c39909c6c3ab7f872b33a3bb20714144022ac9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 14:37:19 +0900 Subject: [PATCH 201/489] Simplify `DependencyProvidingContainer` Using an array of tuple for dependencies instead of using children. --- .../TestSceneCatcher.cs | 7 +++--- .../Visual/DependencyProvidingContainer.cs | 24 ++++--------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 0cf5e2b7a0..0a2dff6a21 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -47,15 +47,16 @@ namespace osu.Game.Rulesets.Catch.Tests { Anchor = Anchor.Centre, }; + droppedObjectContainer = new DroppedObjectContainer(); Child = new DependencyProvidingContainer { - Types = new[] + CachedDependencies = new (Type, object)[] { - typeof(DroppedObjectContainer), + (typeof(DroppedObjectContainer), droppedObjectContainer), }, Children = new Drawable[] { - droppedObjectContainer = new DroppedObjectContainer(), + droppedObjectContainer, catcher = new TestCatcher(trailContainer, difficulty), trailContainer }, diff --git a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs index cd3ae1123b..c799cad61a 100644 --- a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs +++ b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; @@ -10,39 +9,24 @@ namespace osu.Game.Tests.Visual { /// /// A which providing ad-hoc dependencies to the child drawables. - /// - /// To provide a dependency, specify the dependency type to , then specify the dependency value to either or . - /// For each type specified in , the first value compatible with the type is selected and provided to the children. - /// /// /// - /// The and values of the dependencies must be set while this is not loaded. + /// The must be set while this is not loaded. /// public class DependencyProvidingContainer : Container { /// - /// The types of the dependencies provided to the children. + /// The dependencies provided to the children. /// // TODO: should be an init-only property when C# 9 - public Type[] Types { get; set; } = Array.Empty(); - - /// - /// The dependency values provided to the children. - /// - public object[] Values { get; set; } = Array.Empty(); + public (Type, object)[] CachedDependencies { get; set; } = Array.Empty<(Type, object)>(); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencyContainer = new DependencyContainer(base.CreateChildDependencies(parent)); - foreach (var type in Types) - { - object value = Values.FirstOrDefault(v => type.IsInstanceOfType(v)) ?? - Children.FirstOrDefault(d => type.IsInstanceOfType(d)) ?? - throw new InvalidOperationException($"The type {type} is specified in this {nameof(DependencyProvidingContainer)}, but no corresponding value is provided."); - + foreach (var (type, value) in CachedDependencies) dependencyContainer.CacheAs(type, value); - } return dependencyContainer; } From bde35d9f211e9cd789eb6499e7321b33f87d1f31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 16:57:12 +0900 Subject: [PATCH 202/489] Rename radio button classes to be local to editor --- .../Editing/TestSceneEditorComposeRadioButtons.cs | 4 ++-- .../Visual/Editing/TestSceneHitObjectComposer.cs | 6 +++--- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 ++-- .../{DrawableRadioButton.cs => EditorRadioButton.cs} | 6 +++--- ...tonCollection.cs => EditorRadioButtonCollection.cs} | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) rename osu.Game/Screens/Edit/Components/RadioButtons/{DrawableRadioButton.cs => EditorRadioButton.cs} (94%) rename osu.Game/Screens/Edit/Components/RadioButtons/{RadioButtonCollection.cs => EditorRadioButtonCollection.cs} (85%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs index 0b52ae2b95..028509ccd4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs @@ -13,8 +13,8 @@ namespace osu.Game.Tests.Visual.Editing { public TestSceneEditorComposeRadioButtons() { - RadioButtonCollection collection; - Add(collection = new RadioButtonCollection + EditorRadioButtonCollection collection; + Add(collection = new EditorRadioButtonCollection { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 67c37413ed..550896270a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -72,10 +72,10 @@ namespace osu.Game.Tests.Visual.Editing AddStep("clear all control points", () => editorBeatmap.ControlPointInfo.Clear()); AddAssert("Tool is selection", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is SelectTool); - AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); + AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); - AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); - AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Click()); + AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Click()); AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c6c112bec8..7ee631674e 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Edit private InputManager inputManager; - private RadioButtonCollection toolboxCollection; + private EditorRadioButtonCollection toolboxCollection; private FillFlowContainer togglesCollection; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Edit { new ToolboxGroup("toolbox (1-9)") { - Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } + Child = toolboxCollection = new EditorRadioButtonCollection { RelativeSizeAxes = Axes.X } }, new ToolboxGroup("toggles (Q~P)") { diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs similarity index 94% rename from osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs rename to osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs index bbe77eaa07..d66856ebd8 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs @@ -18,10 +18,10 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.RadioButtons { - public class DrawableRadioButton : OsuButton, IHasTooltip + public class EditorRadioButton : OsuButton, IHasTooltip { /// - /// Invoked when this has been selected. + /// Invoked when this has been selected. /// public Action Selected; @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons [Resolved(canBeNull: true)] private EditorBeatmap editorBeatmap { get; set; } - public DrawableRadioButton(RadioButton button) + public EditorRadioButton(RadioButton button) { Button = button; diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButtonCollection.cs similarity index 85% rename from osu.Game/Screens/Edit/Components/RadioButtons/RadioButtonCollection.cs rename to osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButtonCollection.cs index 16574c0baf..6a7b0c9ef7 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButtonCollection.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Screens.Edit.Components.RadioButtons { - public class RadioButtonCollection : CompositeDrawable + public class EditorRadioButtonCollection : CompositeDrawable { private IReadOnlyList items; @@ -28,13 +28,13 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons } } - private readonly FlowContainer buttonContainer; + private readonly FlowContainer buttonContainer; - public RadioButtonCollection() + public EditorRadioButtonCollection() { AutoSizeAxes = Axes.Y; - InternalChild = buttonContainer = new FillFlowContainer + InternalChild = buttonContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons currentlySelected = null; }; - buttonContainer.Add(new DrawableRadioButton(button)); + buttonContainer.Add(new EditorRadioButton(button)); } } } From b29209d13f6bd66b52f5d2c122001392691dd3c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 17:08:40 +0900 Subject: [PATCH 203/489] Ensure tool is always set back to select tool when beatmap becomes untimed --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 7ee631674e..8090fcbd32 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -67,6 +68,8 @@ namespace osu.Game.Rulesets.Edit private FillFlowContainer togglesCollection; + private IBindable hasTiming; + protected HitObjectComposer(Ruleset ruleset) { Ruleset = ruleset; @@ -160,6 +163,14 @@ namespace osu.Game.Rulesets.Edit base.LoadComplete(); inputManager = GetContainingInputManager(); + + hasTiming = EditorBeatmap.HasTiming.GetBoundCopy(); + hasTiming.BindValueChanged(timing => + { + // it's important this is performed before the similar code in EditorRadioButton disables the button. + if (!timing.NewValue) + setSelectTool(); + }); } public override Playfield Playfield => drawableRulesetWrapper.Playfield; From 13cb658d29e4372b5f986f1caad46adc2d28be91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 18:15:09 +0900 Subject: [PATCH 204/489] Mark identifiers as verbatim strings --- osu.Game/Overlays/Profile/Sections/AboutSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/HistoricalSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/KudosuSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/MedalsSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/RanksSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/RecentSection.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/AboutSection.cs b/osu.Game/Overlays/Profile/Sections/AboutSection.cs index c224d2b1be..d0d9362fd2 100644 --- a/osu.Game/Overlays/Profile/Sections/AboutSection.cs +++ b/osu.Game/Overlays/Profile/Sections/AboutSection.cs @@ -10,6 +10,6 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraMeTitle; - public override string Identifier => "me"; + public override string Identifier => @"me"; } } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index b8fbe73c0c..843ab531be 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraBeatmapsTitle; - public override string Identifier => "beatmaps"; + public override string Identifier => @"beatmaps"; public BeatmapsSection() { diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index cba25c0a8b..203844b6b5 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraHistoricalTitle; - public override string Identifier => "historical"; + public override string Identifier => @"historical"; public HistoricalSection() { diff --git a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs index ffa7ef4eaf..5b749c78a8 100644 --- a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs +++ b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraKudosuTitle; - public override string Identifier => "kudosu"; + public override string Identifier => @"kudosu"; public KudosuSection() { diff --git a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs index 3512333e27..cacdd44b61 100644 --- a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs @@ -10,6 +10,6 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraMedalsTitle; - public override string Identifier => "medals"; + public override string Identifier => @"medals"; } } diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index 5e0648dbb1..00a68d5bf9 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraTopRanksTitle; - public override string Identifier => "top_ranks"; + public override string Identifier => @"top_ranks"; public RanksSection() { diff --git a/osu.Game/Overlays/Profile/Sections/RecentSection.cs b/osu.Game/Overlays/Profile/Sections/RecentSection.cs index 7a6536c3af..33d435aa1b 100644 --- a/osu.Game/Overlays/Profile/Sections/RecentSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RecentSection.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraRecentActivityTitle; - public override string Identifier => "recent_activity"; + public override string Identifier => @"recent_activity"; public RecentSection() { From 443058f87994c2db0d8cbe031fd943c75f7d67e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 18:41:29 +0900 Subject: [PATCH 205/489] Move playfield constant to top of file and make `internal` --- osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index ac11bd9918..f1cdb39e91 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -28,6 +28,12 @@ namespace osu.Game.Rulesets.Catch.Objects /// public class JuiceStreamPath { + /// + /// The height of legacy osu!standard playfield. + /// The sliders converted by are vertically contained in this height. + /// + internal const float OSU_PLAYFIELD_HEIGHT = 384; + /// /// The list of vertices of the path, which is represented as a polyline connecting the vertices. /// @@ -211,12 +217,6 @@ namespace osu.Game.Rulesets.Catch.Objects invalidate(); } - /// - /// The height of legacy osu!standard playfield. - /// The sliders converted by are vertically contained in this height. - /// - public const float OSU_PLAYFIELD_HEIGHT = 384; - /// /// Convert the path of this to a and write the result to . /// The resulting slider is "folded" to make it vertically contained in the playfield `(0..)` assuming the slider start position is . From f16b4957aae84870d90c7e964e972779d01e702e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 19:18:04 +0900 Subject: [PATCH 206/489] Move clone to earlier in the process --- osu.Game/Screens/Play/Player.cs | 8 +++++--- osu.Game/Screens/Play/SoloPlayer.cs | 6 ++---- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 51f1dbd121..2328390d76 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -694,9 +694,11 @@ namespace osu.Game.Screens.Play /// The final score. private async Task prepareScoreForResults() { + var scoreCopy = Score.DeepClone(); + try { - await PrepareScoreForResultsAsync(Score).ConfigureAwait(false); + await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); } catch (Exception ex) { @@ -705,14 +707,14 @@ namespace osu.Game.Screens.Play try { - await ImportScore(Score).ConfigureAwait(false); + await ImportScore(scoreCopy).ConfigureAwait(false); } catch (Exception ex) { Logger.Error(ex, @"Score import failed!"); } - return Score.ScoreInfo; + return scoreCopy.ScoreInfo; } /// diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index 7d15fcf49f..d90e8e0168 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -38,15 +38,13 @@ namespace osu.Game.Screens.Play protected override APIRequest CreateSubmissionRequest(Score score, long token) { - var scoreCopy = score.DeepClone(); - - var beatmap = scoreCopy.ScoreInfo.Beatmap; + var beatmap = score.ScoreInfo.Beatmap; Debug.Assert(beatmap.OnlineBeatmapID != null); int beatmapId = beatmap.OnlineBeatmapID.Value; - return new SubmitSoloScoreRequest(beatmapId, token, scoreCopy.ScoreInfo); + return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); } } } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 76e9f28dae..65622a4702 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Screens.Play { var exiting = base.OnExiting(next); - submitScore(Score); + submitScore(Score.DeepClone()); return exiting; } From b3f60c82535fe292cd172b291449cb0d6a61a878 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 19:28:35 +0900 Subject: [PATCH 207/489] Fix date being updated on replays unexpectedly --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 1 - osu.Game/Screens/Play/Player.cs | 8 +------- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 ++ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index eeb881cd39..6a2601170c 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -338,7 +338,6 @@ namespace osu.Game.Rulesets.Scoring score.MaxCombo = HighestCombo.Value; score.Accuracy = Accuracy.Value; score.Rank = Rank.Value; - score.Date = DateTimeOffset.Now; foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.IsScorable())) score.Statistics[result] = GetStatistic(result); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2328390d76..dbe8d530b9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1019,13 +1019,7 @@ namespace osu.Game.Screens.Play /// /// The to prepare. /// A task that prepares the provided score. On completion, the score is assumed to be ready for display. - protected virtual Task PrepareScoreForResultsAsync(Score score) - { - // perform one final population to ensure everything is up-to-date. - ScoreProcessor.PopulateScore(score.ScoreInfo); - - return Task.CompletedTask; - } + protected virtual Task PrepareScoreForResultsAsync(Score score) => Task.CompletedTask; /// /// Creates the for a . diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 65622a4702..5faa384d03 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -109,6 +109,8 @@ namespace osu.Game.Screens.Play { await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false); + score.ScoreInfo.Date = DateTimeOffset.Now; + await submitScore(score).ConfigureAwait(false); } From 97059a9f50afafe091ffed155afd287e4970854d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 19:44:40 +0900 Subject: [PATCH 208/489] Create `Catcher` in `CatchPlayfield` --- .../TestSceneCatcherArea.cs | 5 ++- .../TestSceneHyperDashColouring.cs | 7 ++-- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 30 ++++++++++------ osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 36 +++++++++++-------- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 877e115e2f..18a6e0a66f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -123,8 +123,11 @@ namespace osu.Game.Rulesets.Catch.Tests private readonly DroppedObjectContainer droppedObjectContainer; public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) - : base(beatmapDifficulty) { + MovableCatcher = new Catcher(this, beatmapDifficulty) + { + X = CatchPlayfield.CENTER_X + }; AddInternal(droppedObjectContainer = new DroppedObjectContainer()); } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index e7b0259ea2..61057f4c4d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -213,8 +213,11 @@ namespace osu.Game.Rulesets.Catch.Tests public TestCatcherArea() { - Scale = new Vector2(4f); - + MovableCatcher = new Catcher(this, new BeatmapDifficulty()) + { + X = CatchPlayfield.CENTER_X, + Scale = new Vector2(4) + }; AddInternal(droppedObjectContainer = new DroppedObjectContainer()); } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 05cd29dff5..727d3aca0f 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -26,31 +27,40 @@ namespace osu.Game.Rulesets.Catch.UI /// public const float CENTER_X = WIDTH / 2; - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - - internal readonly CatcherArea CatcherArea; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => // only check the X position; handle all vertical space. base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y)); + internal readonly Catcher Catcher; + + internal readonly CatcherArea CatcherArea; + + [Cached] + private readonly DroppedObjectContainer droppedObjectContainer; + public CatchPlayfield(BeatmapDifficulty difficulty) { - CatcherArea = new CatcherArea(difficulty) + var trailContainer = new Container(); + + Catcher = new Catcher(trailContainer, difficulty) { - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, + X = CENTER_X }; InternalChildren = new[] { droppedObjectContainer = new DroppedObjectContainer(), - CatcherArea.MovableCatcher.CreateProxiedContent(), + Catcher.CreateProxiedContent(), HitObjectContainer.CreateProxy(), // This ordering (`CatcherArea` before `HitObjectContainer`) is important to // make sure the up-to-date catcher position is used for the catcher catching logic of hit objects. - CatcherArea, + CatcherArea = new CatcherArea + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + MovableCatcher = Catcher + }, + trailContainer, HitObjectContainer, }; } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index fea314df8d..a9d0275678 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Replays; @@ -20,9 +19,22 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 106.75f; - public readonly Catcher MovableCatcher; + public Catcher MovableCatcher + { + get => catcher; + set + { + if (catcher != null) + Remove(catcher); + + Add(catcher = value); + } + } + private readonly CatchComboDisplay comboDisplay; + private Catcher catcher; + /// /// -1 when only left button is pressed. /// 1 when only right button is pressed. @@ -30,21 +42,17 @@ namespace osu.Game.Rulesets.Catch.UI /// private int currentDirection; - public CatcherArea(BeatmapDifficulty difficulty = null) + public CatcherArea() { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); - Children = new Drawable[] + Child = comboDisplay = new CatchComboDisplay { - comboDisplay = new CatchComboDisplay - { - RelativeSizeAxes = Axes.None, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopLeft, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 350f }, - X = CatchPlayfield.CENTER_X - }, - MovableCatcher = new Catcher(this, difficulty) { X = CatchPlayfield.CENTER_X }, + RelativeSizeAxes = Axes.None, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopLeft, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 350f }, + X = CatchPlayfield.CENTER_X }; } From 50f9e5f3629ce7003a54456daf2bfcea8ed3f588 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 19:52:40 +0900 Subject: [PATCH 209/489] Replace usage of `CatcherArea.MovableCatcher` with `Catcher` --- osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs | 4 ++-- osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs | 4 +--- osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index fd6a9c7b7b..e7c7dc3c98 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Catch.Tests private bool playfieldIsEmpty => !((CatchPlayfield)drawableRuleset.Playfield).AllHitObjects.Any(h => h.IsAlive); - private CatcherAnimationState catcherState => ((CatchPlayfield)drawableRuleset.Playfield).CatcherArea.MovableCatcher.CurrentState; + private CatcherAnimationState catcherState => ((CatchPlayfield)drawableRuleset.Playfield).Catcher.CurrentState; private void spawnFruits(bool hit = false) { @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Catch.Tests float xCoords = CatchPlayfield.CENTER_X; if (drawableRuleset.Playfield is CatchPlayfield catchPlayfield) - catchPlayfield.CatcherArea.MovableCatcher.X = xCoords - x_offset; + catchPlayfield.Catcher.X = xCoords - x_offset; if (hit) xCoords -= x_offset; diff --git a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs index d383eb9ba6..8c9f292aa9 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Edit base.LoadComplete(); // TODO: honor "hit animation" setting? - CatcherArea.MovableCatcher.CatchFruitOnPlate = false; + Catcher.CatchFruitOnPlate = false; // TODO: disable hit lighting as well } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index 71268d899d..f399f48ebd 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -41,9 +41,7 @@ namespace osu.Game.Rulesets.Catch.Mods { base.Update(); - var catcherArea = playfield.CatcherArea; - - FlashlightPosition = catcherArea.ToSpaceOfOtherDrawable(catcherArea.MovableCatcher.DrawPosition, this); + FlashlightPosition = playfield.CatcherArea.ToSpaceOfOtherDrawable(playfield.Catcher.DrawPosition, this); } private float getSizeFor(int combo) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index f9e106f097..d48edbcd74 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Mods var drawableCatchRuleset = (DrawableCatchRuleset)drawableRuleset; var catchPlayfield = (CatchPlayfield)drawableCatchRuleset.Playfield; - catchPlayfield.CatcherArea.MovableCatcher.CatchFruitOnPlate = false; + catchPlayfield.Catcher.CatchFruitOnPlate = false; } protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 727d3aca0f..21a96d6635 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.UI ((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch; } - private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.MovableCatcher.CanCatch(obj); + private bool checkIfWeCanCatch(CatchHitObject obj) => Catcher.CanCatch(obj); private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) => CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result); diff --git a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs index 1ddb5ac630..a7879846df 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs @@ -21,6 +21,6 @@ namespace osu.Game.Rulesets.Catch.UI } protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) - => new CatchReplayFrame(Time.Current, playfield.CatcherArea.MovableCatcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame); + => new CatchReplayFrame(Time.Current, playfield.Catcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame); } } From 063f14da98b0491a874b9014c230f5e96e830b07 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 19:55:08 +0900 Subject: [PATCH 210/489] Update test room manager to not return passwords --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 12 ++--- .../Multiplayer/TestMultiplayerRoomManager.cs | 47 ++++++++++++------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index 53bd2a6106..0111882d9a 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -9,23 +9,23 @@ namespace osu.Game.Online.Rooms { public class JoinRoomRequest : APIRequest { - private readonly Room room; - private readonly string password; + public readonly Room Room; + public readonly string Password; public JoinRoomRequest(Room room, string password) { - this.room = room; - this.password = password; + Room = room; + Password = password; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - req.AddParameter("password", password); + req.AddParameter("password", Password); return req; } - protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}"; + protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}"; } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 5d66cdba02..77238f47fb 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -45,21 +45,33 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (req) { case CreateRoomRequest createRoomRequest: - var createdRoom = new APICreatedRoom(); + var apiRoom = new Room(); - createdRoom.CopyFrom(createRoomRequest.Room); - createdRoom.RoomID.Value ??= currentRoomId++; + apiRoom.CopyFrom(createRoomRequest.Room); + apiRoom.RoomID.Value ??= currentRoomId++; + for (int i = 0; i < apiRoom.Playlist.Count; i++) + apiRoom.Playlist[i].ID = currentPlaylistItemId++; - for (int i = 0; i < createdRoom.Playlist.Count; i++) - createdRoom.Playlist[i].ID = currentPlaylistItemId++; + var responseRoom = new APICreatedRoom(); + responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); - Rooms.Add(createdRoom); - createRoomRequest.TriggerSuccess(createdRoom); + Rooms.Add(apiRoom); + createRoomRequest.TriggerSuccess(responseRoom); return true; case JoinRoomRequest joinRoomRequest: + { + var room = Rooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); + + if (joinRoomRequest.Password != room.Password.Value) + { + joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password.")); + return true; + } + joinRoomRequest.TriggerSuccess(); return true; + } case PartRoomRequest partRoomRequest: partRoomRequest.TriggerSuccess(); @@ -69,20 +81,13 @@ namespace osu.Game.Tests.Visual.Multiplayer var roomsWithoutParticipants = new List(); foreach (var r in Rooms) - { - var newRoom = new Room(); - - newRoom.CopyFrom(r); - newRoom.RecentParticipants.Clear(); - - roomsWithoutParticipants.Add(newRoom); - } + roomsWithoutParticipants.Add(createResponseRoom(r, false)); getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); return true; case GetRoomRequest getRoomRequest: - getRoomRequest.TriggerSuccess(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId)); + getRoomRequest.TriggerSuccess(createResponseRoom(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); return true; case GetBeatmapSetRequest getBeatmapSetRequest: @@ -118,6 +123,16 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } + private Room createResponseRoom(Room room, bool withParticipants) + { + var responseRoom = new Room(); + responseRoom.CopyFrom(room); + responseRoom.Password.Value = null; + if (!withParticipants) + responseRoom.RecentParticipants.Clear(); + return responseRoom; + } + public new void ClearRooms() => base.ClearRooms(); public new void Schedule(Action action) => base.Schedule(action); From 8c0daa89a0ec0d027c0ba20d6d131a94073d5259 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:01:44 +0900 Subject: [PATCH 211/489] Make test multiplayer client validate password --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index adc632a2b1..82cb68b452 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -119,6 +119,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == roomId); + if (password != apiRoom.Password.Value) + throw new InvalidOperationException("Invalid password."); + var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value From 2515785f933682593721279519855552507085a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:02:14 +0900 Subject: [PATCH 212/489] Use room password to fill settings textbox --- osu.Game/Online/Rooms/Room.cs | 2 +- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 1 + osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index f79a410bd9..48e9f94dd5 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -85,6 +85,7 @@ namespace osu.Game.Online.Rooms #region Properties only used for room creation request + [Cached(Name = nameof(Password))] [JsonProperty("password")] public readonly Bindable Password = new Bindable(); @@ -159,7 +160,6 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; Availability.Value = other.Availability.Value; - Password.Value = other.Password.Value; HasPassword.Value = other.HasPassword.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index fceb124e0a..338d2c9e84 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -271,6 +271,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); RoomID.BindValueChanged(roomId => initialBeatmapControl.Alpha = roomId.NewValue == null ? 1 : 0, true); + Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true); operationInProgress.BindTo(ongoingOperationTracker.InProgress); operationInProgress.BindValueChanged(v => diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index eb0b23f13f..0b28bc1a7e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -56,6 +56,9 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable Availability { get; private set; } + [Resolved(typeof(Room), nameof(Room.Password))] + public Bindable Password { get; private set; } + [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } From 26d0eea4854d836e7af67617b7c2688f4d3d7b2a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:03:00 +0900 Subject: [PATCH 213/489] Set HasPassword correctly in the response room --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 77238f47fb..c94c0b8683 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -127,6 +127,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { var responseRoom = new Room(); responseRoom.CopyFrom(room); + responseRoom.HasPassword.Value = !string.IsNullOrEmpty(responseRoom.Password.Value); responseRoom.Password.Value = null; if (!withParticipants) responseRoom.RecentParticipants.Clear(); From a5a0f12e199f4fc2b7fe171f0879b4974e8744f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:07:56 +0900 Subject: [PATCH 214/489] Also copy password in test room manager --- .../Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index c94c0b8683..59679f3d66 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -49,6 +49,11 @@ namespace osu.Game.Tests.Visual.Multiplayer apiRoom.CopyFrom(createRoomRequest.Room); apiRoom.RoomID.Value ??= currentRoomId++; + + // Passwords are explicitly not copied between rooms. + apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); + apiRoom.Password.Value = createRoomRequest.Room.Password.Value; + for (int i = 0; i < apiRoom.Playlist.Count; i++) apiRoom.Playlist[i].ID = currentPlaylistItemId++; @@ -127,7 +132,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { var responseRoom = new Room(); responseRoom.CopyFrom(room); - responseRoom.HasPassword.Value = !string.IsNullOrEmpty(responseRoom.Password.Value); responseRoom.Password.Value = null; if (!withParticipants) responseRoom.RecentParticipants.Clear(); From 1b9d297911e071b569e64f31428a377384c41353 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:08:29 +0900 Subject: [PATCH 215/489] Add test --- .../Multiplayer/TestSceneMultiplayer.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7673efb78f..f45ecca424 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -85,6 +85,26 @@ namespace osu.Game.Tests.Visual.Multiplayer // used to test the flow of multiplayer from visual tests. } + [Test] + public void TestCreateRoomWithPassword() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Password = { Value = "password" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddAssert("room has password", () => client.APIRoom?.Password.Value == "password"); + } + [Test] public void TestUserSetToIdleWhenBeatmapDeleted() { From b88ee3c1a1e183a6f153b17c0a4aac8b641822e5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:11:49 +0900 Subject: [PATCH 216/489] Pass `DroppedObjectContainer` via constructor instead of DI It is now just one level deep, so it is not beneficial to use DI here. This effectively reverts ae09c23e. --- .../TestSceneCatchSkinConfiguration.cs | 7 +-- .../TestSceneCatcher.cs | 35 +++++++-------- .../TestSceneCatcherArea.cs | 10 ++--- .../TestSceneHyperDashColouring.cs | 43 ++++++++----------- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 10 ++--- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 +-- 6 files changed, 46 insertions(+), 65 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs index ec186bcfb2..83f28086e6 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -24,16 +23,12 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneCatchSkinConfiguration : OsuTestScene { - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - private Catcher catcher; private readonly Container container; public TestSceneCatchSkinConfiguration() { - Add(droppedObjectContainer = new DroppedObjectContainer()); Add(container = new Container { RelativeSizeAxes = Axes.Both }); } @@ -46,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests var skin = new TestSkin { FlipCatcherPlate = flip }; container.Child = new SkinProvidingContainer(skin) { - Child = catcher = new Catcher(new Container()) + Child = catcher = new Catcher(new Container(), new DroppedObjectContainer()) { Anchor = Anchor.Centre } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 8359657f84..b4282e6784 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,23 +31,12 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; + private Container trailContainer; - private readonly Container trailContainer; + private DroppedObjectContainer droppedObjectContainer; private TestCatcher catcher; - public TestSceneCatcher() - { - Add(trailContainer = new Container - { - Anchor = Anchor.Centre, - Depth = -1 - }); - Add(droppedObjectContainer = new DroppedObjectContainer()); - } - [SetUp] public void SetUp() => Schedule(() => { @@ -56,13 +45,19 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - if (catcher != null) - Remove(catcher); + trailContainer = new Container(); + droppedObjectContainer = new DroppedObjectContainer(); - Add(catcher = new TestCatcher(trailContainer, difficulty) + Child = new Container { - Anchor = Anchor.Centre - }); + Anchor = Anchor.Centre, + Children = new Drawable[] + { + droppedObjectContainer, + catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty), + trailContainer, + } + }; }); [Test] @@ -299,8 +294,8 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(Container trailsTarget, BeatmapDifficulty difficulty) - : base(trailsTarget, difficulty) + public TestCatcher(Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) + : base(trailsTarget, droppedObjectTarget, difficulty) { } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 18a6e0a66f..5a2e5b60f5 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -119,16 +119,16 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestCatcherArea : CatcherArea { - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) { - MovableCatcher = new Catcher(this, beatmapDifficulty) + var droppedObjectContainer = new DroppedObjectContainer(); + + Add(droppedObjectContainer); + + MovableCatcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; - AddInternal(droppedObjectContainer = new DroppedObjectContainer()); } public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 61057f4c4d..73797d0a6a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -113,36 +113,45 @@ namespace osu.Game.Rulesets.Catch.Tests private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) { - CatcherArea catcherArea = null; + Container trailsContainer = null; + Catcher catcher = null; CatcherTrailDisplay trails = null; AddStep("create hyper-dashing catcher", () => { - Child = setupSkinHierarchy(catcherArea = new TestCatcherArea + trailsContainer = new Container(); + Child = setupSkinHierarchy(new Container { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Children = new Drawable[] + { + catcher = new Catcher(trailsContainer, new DroppedObjectContainer()) + { + Scale = new Vector2(4) + }, + trailsContainer + } }, skin); }); AddStep("get trails container", () => { - trails = catcherArea.OfType().Single(); - catcherArea.MovableCatcher.SetHyperDashState(2); + trails = trailsContainer.OfType().Single(); + catcher.SetHyperDashState(2); }); - AddUntilStep("catcher colour is correct", () => catcherArea.MovableCatcher.Colour == expectedCatcherColour); + AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour); AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); AddStep("finish hyper-dashing", () => { - catcherArea.MovableCatcher.SetHyperDashState(); - catcherArea.MovableCatcher.FinishTransforms(); + catcher.SetHyperDashState(); + catcher.FinishTransforms(); }); - AddAssert("catcher colour returned to white", () => catcherArea.MovableCatcher.Colour == Color4.White); + AddAssert("catcher colour returned to white", () => catcher.Colour == Color4.White); } private void checkHyperDashFruitColour(ISkin skin, Color4 expectedColour) @@ -205,21 +214,5 @@ namespace osu.Game.Rulesets.Catch.Tests { } } - - private class TestCatcherArea : CatcherArea - { - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - - public TestCatcherArea() - { - MovableCatcher = new Catcher(this, new BeatmapDifficulty()) - { - X = CatchPlayfield.CENTER_X, - Scale = new Vector2(4) - }; - AddInternal(droppedObjectContainer = new DroppedObjectContainer()); - } - } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 21a96d6635..27bbc32fd9 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -35,21 +35,19 @@ namespace osu.Game.Rulesets.Catch.UI internal readonly CatcherArea CatcherArea; - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - public CatchPlayfield(BeatmapDifficulty difficulty) { var trailContainer = new Container(); + var droppedObjectContainer = new DroppedObjectContainer(); - Catcher = new Catcher(trailContainer, difficulty) + Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty) { X = CENTER_X }; InternalChildren = new[] { - droppedObjectContainer = new DroppedObjectContainer(), + droppedObjectContainer, Catcher.CreateProxiedContent(), HitObjectContainer.CreateProxy(), // This ordering (`CatcherArea` before `HitObjectContainer`) is important to @@ -58,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.UI { Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, - MovableCatcher = Catcher + MovableCatcher = Catcher, }, trailContainer, HitObjectContainer, diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 57523d3505..a595536a06 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -74,8 +74,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Contains objects dropped from the plate. /// - [Resolved] - private DroppedObjectContainer droppedObjectTarget { get; set; } + private readonly DroppedObjectContainer droppedObjectTarget; public CatcherAnimationState CurrentState { @@ -134,9 +133,10 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; + this.droppedObjectTarget = droppedObjectTarget; Origin = Anchor.TopCentre; From 879467961f9266ba7de239dccb3642ea8361aaf5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:13:31 +0900 Subject: [PATCH 217/489] Fix catcher trails displayed in wrong place --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 27bbc32fd9..19538aacde 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -37,7 +37,11 @@ namespace osu.Game.Rulesets.Catch.UI public CatchPlayfield(BeatmapDifficulty difficulty) { - var trailContainer = new Container(); + var trailContainer = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft + }; var droppedObjectContainer = new DroppedObjectContainer(); Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty) From 7201cfe0b4b36eddad1188f7c94908ed1d1af8ae Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:18:17 +0900 Subject: [PATCH 218/489] Move child drawable creation of `CatchPlayfield` from constructor to `load`. --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 19538aacde..dcbac47505 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchPlayfield : ScrollingPlayfield { + private readonly BeatmapDifficulty difficulty; + /// /// The width of the playfield. /// The horizontal movement of the catcher is confined in the area of this width. @@ -31,11 +33,17 @@ namespace osu.Game.Rulesets.Catch.UI // only check the X position; handle all vertical space. base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y)); - internal readonly Catcher Catcher; + internal Catcher Catcher { get; private set; } - internal readonly CatcherArea CatcherArea; + internal CatcherArea CatcherArea { get; private set; } public CatchPlayfield(BeatmapDifficulty difficulty) + { + this.difficulty = difficulty; + } + + [BackgroundDependencyLoader] + private void load() { var trailContainer = new Container { @@ -49,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.UI X = CENTER_X }; - InternalChildren = new[] + AddRangeInternal(new[] { droppedObjectContainer, Catcher.CreateProxiedContent(), @@ -64,12 +72,8 @@ namespace osu.Game.Rulesets.Catch.UI }, trailContainer, HitObjectContainer, - }; - } + }); - [BackgroundDependencyLoader] - private void load() - { RegisterPool(50); RegisterPool(50); RegisterPool(100); From d6aa15e5d7c92df054e29a12b6ff2d00068bec8b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:19:23 +0900 Subject: [PATCH 219/489] Remove local APIRoom from test multiplayer client --- .../Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 82cb68b452..1528ed0bc8 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public override IBindable IsConnected => isConnected; private readonly Bindable isConnected = new Bindable(true); - public Room? APIRoom { get; private set; } + public new Room? APIRoom => base.APIRoom; public Action? RoomSetupAction; @@ -138,6 +138,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RequiredMods = apiRoom.Playlist.Last().RequiredMods.Select(m => new APIMod(m)).ToArray(), AllowedMods = apiRoom.Playlist.Last().AllowedMods.Select(m => new APIMod(m)).ToArray(), PlaylistItemId = apiRoom.Playlist.Last().ID, + // ReSharper disable once ConstantNullCoalescingCondition Incorrect inspection due to lack of nullable in Room.cs. Password = password ?? string.Empty, }, Users = { localUser }, @@ -147,16 +148,10 @@ namespace osu.Game.Tests.Visual.Multiplayer RoomSetupAction?.Invoke(room); RoomSetupAction = null; - APIRoom = apiRoom; - return Task.FromResult(room); } - protected override Task LeaveRoomInternal() - { - APIRoom = null; - return Task.CompletedTask; - } + protected override Task LeaveRoomInternal() => Task.CompletedTask; public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(userId); From 2eec524f2744e68705f9ede888ca57796b2bae4d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:20:08 +0900 Subject: [PATCH 220/489] Fix password not copied from multiplayer client --- .../Multiplayer/TestSceneMultiplayer.cs | 21 ++++++++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 29 ++++++++++--------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index f45ecca424..4ea628cb55 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -105,6 +105,27 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room has password", () => client.APIRoom?.Password.Value == "password"); } + [Test] + public void TestLocalPasswordUpdatedWhenMultiplayerSettingsChange() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Password = { Value = "password" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddStep("change password", () => client.ChangeSettings(password: "password2")); + AddUntilStep("local password changed", () => client.APIRoom?.Password.Value == "password2"); + } + [Test] public void TestUserSetToIdleWhenBeatmapDeleted() { diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 42d436ef11..9972d7e88d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -92,7 +92,7 @@ namespace osu.Game.Online.Multiplayer [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; - private Room? apiRoom; + protected Room? APIRoom { get; private set; } [BackgroundDependencyLoader] private void load() @@ -139,7 +139,7 @@ namespace osu.Game.Online.Multiplayer await scheduleAsync(() => { Room = joinedRoom; - apiRoom = room; + APIRoom = room; foreach (var user in joinedRoom.Users) updateUserPlayingState(user.UserID, user.State); }, cancellationSource.Token).ConfigureAwait(false); @@ -168,7 +168,7 @@ namespace osu.Game.Online.Multiplayer // For example, if a room was left and the user immediately pressed the "create room" button, then the user could be taken into the lobby if the value of Room is not reset in time. var scheduledReset = scheduleAsync(() => { - apiRoom = null; + APIRoom = null; Room = null; CurrentMatchPlayingUserIds.Clear(); @@ -305,22 +305,22 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); Room.State = state; switch (state) { case MultiplayerRoomState.Open: - apiRoom.Status.Value = new RoomStatusOpen(); + APIRoom.Status.Value = new RoomStatusOpen(); break; case MultiplayerRoomState.Playing: - apiRoom.Status.Value = new RoomStatusPlaying(); + APIRoom.Status.Value = new RoomStatusPlaying(); break; case MultiplayerRoomState.Closed: - apiRoom.Status.Value = new RoomStatusEnded(); + APIRoom.Status.Value = new RoomStatusEnded(); break; } @@ -381,12 +381,12 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); var user = Room.Users.FirstOrDefault(u => u.UserID == userId); Room.Host = user; - apiRoom.Host.Value = user?.User; + APIRoom.Host.Value = user?.User; RoomUpdated?.Invoke(); }, false); @@ -529,11 +529,12 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); // Update a few properties of the room instantaneously. Room.Settings = settings; - apiRoom.Name.Value = Room.Settings.Name; + APIRoom.Name.Value = Room.Settings.Name; + APIRoom.Password.Value = Room.Settings.Password; // The current item update is delayed until an online beatmap lookup (below) succeeds. // In-order for the client to not display an outdated beatmap, the current item is forcefully cleared here. @@ -555,7 +556,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null || !Room.Settings.Equals(settings)) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); beatmap.MD5Hash = settings.BeatmapChecksum; @@ -565,7 +566,7 @@ namespace osu.Game.Online.Multiplayer var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset)); // Try to retrieve the existing playlist item from the API room. - var playlistItem = apiRoom.Playlist.FirstOrDefault(i => i.ID == settings.PlaylistItemId); + var playlistItem = APIRoom.Playlist.FirstOrDefault(i => i.ID == settings.PlaylistItemId); if (playlistItem != null) updateItem(playlistItem); @@ -573,7 +574,7 @@ namespace osu.Game.Online.Multiplayer { // An existing playlist item does not exist, so append a new one. updateItem(playlistItem = new PlaylistItem()); - apiRoom.Playlist.Add(playlistItem); + APIRoom.Playlist.Add(playlistItem); } CurrentMatchPlayingItem.Value = playlistItem; From 41169fbdaffbac67917cc0de15b7608021526088 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:20:10 +0900 Subject: [PATCH 221/489] Add `[NotNull]` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a595536a06..a671bce590 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] Container trailsTarget, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; this.droppedObjectTarget = droppedObjectTarget; From 80c2b1449bb2ae088104a37712b17829a32893d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 20:27:00 +0900 Subject: [PATCH 222/489] Fix API request potentially firing failed events after completion Specifically, `Cancel()` calls were not thread safe. Due to a series of events, `ListPollingComponent` could call `Cancel` from a non-update thread, leading to a race condition where both a `Success` and `Fail` event can be fired. This is intended to be the simplest fix possible, locking and guarding specifically on the callbacks. Further work could be done in the future to improve the flow surrounding `pendingFailure`, potentially reducing redundant work and cleaning up the code, but that's not happening here. Closes https://github.com/ppy/osu/issues/13632. --- osu.Game/Online/API/APIException.cs | 15 +++++ osu.Game/Online/API/APIRequest.cs | 58 ++++++++++++------- .../Online/API/APIRequestCompletionState.cs | 23 ++++++++ osu.Game/Online/PollingComponent.cs | 2 +- 4 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Online/API/APIException.cs create mode 100644 osu.Game/Online/API/APIRequestCompletionState.cs diff --git a/osu.Game/Online/API/APIException.cs b/osu.Game/Online/API/APIException.cs new file mode 100644 index 0000000000..97786bced9 --- /dev/null +++ b/osu.Game/Online/API/APIException.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.API +{ + public class APIException : InvalidOperationException + { + public APIException(string messsage, Exception innerException) + : base(messsage, innerException) + { + } + } +} diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 1a6868cfa4..8d816d3975 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -79,7 +79,13 @@ namespace osu.Game.Online.API /// public event APIFailureHandler Failure; - private bool cancelled; + private readonly object completionStateLock = new object(); + + /// + /// The state of this request, from an outside perspective. + /// This is used to ensure correct notification events are fired. + /// + private APIRequestCompletionState completionState; private Action pendingFailure; @@ -116,12 +122,7 @@ namespace osu.Game.Online.API PostProcess(); - API.Schedule(delegate - { - if (cancelled) return; - - TriggerSuccess(); - }); + API.Schedule(TriggerSuccess); } /// @@ -131,16 +132,29 @@ namespace osu.Game.Online.API { } - private bool succeeded; - internal virtual void TriggerSuccess() { - succeeded = true; + lock (completionStateLock) + { + if (completionState != APIRequestCompletionState.Waiting) + return; + + completionState = APIRequestCompletionState.Completed; + } + Success?.Invoke(); } internal void TriggerFailure(Exception e) { + lock (completionStateLock) + { + if (completionState != APIRequestCompletionState.Waiting) + return; + + completionState = APIRequestCompletionState.Failed; + } + Failure?.Invoke(e); } @@ -148,10 +162,14 @@ namespace osu.Game.Online.API public void Fail(Exception e) { - if (succeeded || cancelled) - return; + lock (completionStateLock) + { + // while it doesn't matter if code following this check is run more than once, + // this avoids unnecessarily performing work where we are already sure the user has been informed. + if (completionState != APIRequestCompletionState.Waiting) + return; + } - cancelled = true; WebRequest?.Abort(); string responseString = WebRequest?.GetResponseString(); @@ -181,7 +199,11 @@ namespace osu.Game.Online.API /// Whether we are in a failed or cancelled state. private bool checkAndScheduleFailure() { - if (pendingFailure == null) return cancelled; + lock (completionStateLock) + { + if (pendingFailure == null) + return completionState == APIRequestCompletionState.Failed; + } if (API == null) pendingFailure(); @@ -199,14 +221,6 @@ namespace osu.Game.Online.API } } - public class APIException : InvalidOperationException - { - public APIException(string messsage, Exception innerException) - : base(messsage, innerException) - { - } - } - public delegate void APIFailureHandler(Exception e); public delegate void APISuccessHandler(); diff --git a/osu.Game/Online/API/APIRequestCompletionState.cs b/osu.Game/Online/API/APIRequestCompletionState.cs new file mode 100644 index 0000000000..84c9974dd8 --- /dev/null +++ b/osu.Game/Online/API/APIRequestCompletionState.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.API +{ + public enum APIRequestCompletionState + { + /// + /// Not yet run or currently waiting on response. + /// + Waiting, + + /// + /// Ran to completion. + /// + Completed, + + /// + /// Cancelled or failed due to error. + /// + Failed + } +} diff --git a/osu.Game/Online/PollingComponent.cs b/osu.Game/Online/PollingComponent.cs index 3d19f2ab09..806c0047e7 100644 --- a/osu.Game/Online/PollingComponent.cs +++ b/osu.Game/Online/PollingComponent.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online return true; } - // not ennough time has passed since the last poll. we do want to schedule a poll to happen, though. + // not enough time has passed since the last poll. we do want to schedule a poll to happen, though. scheduleNextPoll(); return false; } From 5fc13975643b0d517634358c70a56dad974737b5 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 09:55:40 +0800 Subject: [PATCH 223/489] Apply suggestion from code review Co-authored-by: frenzibyte --- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 3 ++- osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs | 1 - osu.Game/Overlays/Wiki/WikiHeader.cs | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index a282c77bd6..56e852fe80 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogUpdateStreamControl Streams; - public LocalisableString ListingString => LayoutStrings.HeaderChangelogIndex; + public static LocalisableString ListingString => LayoutStrings.HeaderChangelogIndex; private Box streamsBackground; diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index b6e49811ad..7e10d855fe 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -12,7 +12,8 @@ namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - public LocalisableString FrontPageString => osu.Game.Resources.Localisation.Web.NewsStrings.IndexTitleInfo; + public static LocalisableString FrontPageString => NewsStrings.IndexTitleInfo; + public Action ShowFrontPage; private readonly Bindable article = new Bindable(); diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 92750d8806..ba88eec6aa 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -72,5 +72,4 @@ namespace osu.Game.Overlays.Rankings } } } - } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index f24d59a8d3..286711c518 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -14,7 +14,8 @@ namespace osu.Game.Overlays.Wiki public class WikiHeader : BreadcrumbControlOverlayHeader { private const string index_path = "Main_Page"; - public LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; + + public static LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; public readonly Bindable WikiPageData = new Bindable(); From 57eed886018810be9f83c198ee8240b25ffa3595 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 10:12:24 +0800 Subject: [PATCH 224/489] symbol renaming Co-authored-by: frenzibyte --- ...nStrings.cs => NamedOverlayComponentStrings.cs} | 14 +++++++------- .../BeatmapListing/BeatmapListingHeader.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- .../Overlays/Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- .../Overlays/Rankings/RankingsOverlayHeader.cs | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) rename osu.Game/Localisation/{HeaderDescriptionStrings.cs => NamedOverlayComponentStrings.cs} (50%) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs similarity index 50% rename from osu.Game/Localisation/HeaderDescriptionStrings.cs rename to osu.Game/Localisation/NamedOverlayComponentStrings.cs index 6ed703191a..6cd19a0977 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -5,34 +5,34 @@ using osu.Framework.Localisation; namespace osu.Game.Localisation { - public static class HeaderDescriptionStrings + public static class NamedOverlayComponentStrings { - private const string prefix = @"osu.Game.Resources.Localisation.HeaderDescription"; + private const string prefix = @"osu.Game.Resources.Localisation.NamedOverlayComponent"; /// /// "browse for new beatmaps" /// - public static LocalisableString BeatmapListing => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); /// /// "track recent dev updates in the osu! ecosystem" /// - public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); /// /// "view your friends and other information" /// - public static LocalisableString Dashboard => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); /// /// "find out who's the best right now" /// - public static LocalisableString Rankings => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); /// /// "get up-to-date on community happenings" /// - public static LocalisableString News => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); + public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); private static string getKey(string key) => $"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 48ecbce7c1..3568fe9e4f 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.BeatmapListing public BeatmapListingTitle() { Title = PageTitleStrings.MainBeatmapsetsControllerIndex; - Description = HeaderDescriptionStrings.BeatmapListing; + Description = NamedOverlayComponentStrings.BeatmapListingDescription; IconTexture = "Icons/Hexacons/beatmap"; } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 56e852fe80..ef174298c2 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeaderTitle() { Title = LayoutStrings.MenuHomeChangelogIndex; - Description = HeaderDescriptionStrings.Changelog; + Description = NamedOverlayComponentStrings.ChangelogDescription; IconTexture = "Icons/Hexacons/devtools"; } } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 284e95ad84..5e69caad6e 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Dashboard public DashboardTitle() { Title = HomeStrings.UserTitle; - Description = HeaderDescriptionStrings.Dashboard; + Description = NamedOverlayComponentStrings.DashboardDescription; IconTexture = "Icons/Hexacons/social"; } } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 7e10d855fe..23fab6faaf 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.News public NewsHeaderTitle() { Title = LayoutStrings.MenuHomeNewsIndex; - Description = HeaderDescriptionStrings.News; + Description = NamedOverlayComponentStrings.NewsDescription; IconTexture = "Icons/Hexacons/news"; } } diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index ba88eec6aa..812c2f2182 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Rankings public RankingsTitle() { Title = LayoutStrings.MenuRankingsDefault; - Description = HeaderDescriptionStrings.Rankings; + Description = NamedOverlayComponentStrings.RankingsDescription; IconTexture = "Icons/Hexacons/rankings"; } } From 765881d8b0c2c89b6d0934f9d734d81670047643 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Mon, 19 Jul 2021 19:23:26 +0800 Subject: [PATCH 225/489] Move strings --- .../NamedOverlayComponentStrings.cs | 22 +++++++++++++++++++ .../BeatmapListing/BeatmapListingHeader.cs | 3 +-- .../Overlays/Changelog/ChangelogHeader.cs | 2 +- .../Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- .../Rankings/RankingsOverlayHeader.cs | 2 +- osu.Game/Overlays/Wiki/WikiHeader.cs | 5 +++-- 7 files changed, 30 insertions(+), 8 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index 6cd19a0977..0167a1b030 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Localisation { @@ -9,31 +10,52 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.NamedOverlayComponent"; + /// + public static LocalisableString BeatmapListingTitle => PageTitleStrings.MainBeatmapsetsControllerIndex; + /// /// "browse for new beatmaps" /// public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + /// + public static LocalisableString ChangelogTitle => PageTitleStrings.MainChangelogControllerDefault; + /// /// "track recent dev updates in the osu! ecosystem" /// public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + /// + public static LocalisableString DashboardTitle => PageTitleStrings.MainHomeControllerIndex; + /// /// "view your friends and other information" /// public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + /// + public static LocalisableString RankingsTitle => PageTitleStrings.MainRankingControllerDefault; + /// /// "find out who's the best right now" /// public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + /// + public static LocalisableString NewsTitle => PageTitleStrings.MainNewsControllerDefault; + /// /// "get up-to-date on community happenings" /// public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); + /// + public static LocalisableString WikiTitle => LayoutStrings.MenuHelpGetWiki; + + /// + public static LocalisableString WikiDescription => PageTitleStrings.MainWikiControllerDefault; + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 3568fe9e4f..fa2ee0d735 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Localisation; -using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { @@ -14,7 +13,7 @@ namespace osu.Game.Overlays.BeatmapListing { public BeatmapListingTitle() { - Title = PageTitleStrings.MainBeatmapsetsControllerIndex; + Title = NamedOverlayComponentStrings.BeatmapListingTitle; Description = NamedOverlayComponentStrings.BeatmapListingDescription; IconTexture = "Icons/Hexacons/beatmap"; } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index ef174298c2..8707883ddd 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Changelog { public ChangelogHeaderTitle() { - Title = LayoutStrings.MenuHomeChangelogIndex; + Title = NamedOverlayComponentStrings.ChangelogTitle; Description = NamedOverlayComponentStrings.ChangelogDescription; IconTexture = "Icons/Hexacons/devtools"; } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 5e69caad6e..05c9f30ff3 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Dashboard { public DashboardTitle() { - Title = HomeStrings.UserTitle; + Title = NamedOverlayComponentStrings.DashboardTitle; Description = NamedOverlayComponentStrings.DashboardDescription; IconTexture = "Icons/Hexacons/social"; } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 23fab6faaf..bf739e1e8e 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = LayoutStrings.MenuHomeNewsIndex; + Title = NamedOverlayComponentStrings.NewsTitle; Description = NamedOverlayComponentStrings.NewsDescription; IconTexture = "Icons/Hexacons/news"; } diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 812c2f2182..b6c16d398d 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = LayoutStrings.MenuRankingsDefault; + Title = NamedOverlayComponentStrings.RankingsTitle; Description = NamedOverlayComponentStrings.RankingsDescription; IconTexture = "Icons/Hexacons/rankings"; } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 286711c518..4c90551d35 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; @@ -76,8 +77,8 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = LayoutStrings.MenuHelpGetWiki; - Description = PageTitleStrings.MainWikiControllerDefault; + Title = NamedOverlayComponentStrings.WikiTitle; + Description = NamedOverlayComponentStrings.WikiDescription; IconTexture = "Icons/Hexacons/wiki"; } } From 3168a927dc691e6025355cc872fba67c577a3a2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:50:30 +0900 Subject: [PATCH 226/489] Fix possible exception --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 561fa220c8..f35671a761 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -326,7 +326,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. - if (client.Room == null || !client.IsConnected.Value) + if (client?.Room == null || !client.IsConnected.Value) { // room has not been created yet; exit immediately. return base.OnExiting(next); From 05295241b878dc5ac51d4851765af77295fda6f8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:55:14 +0900 Subject: [PATCH 227/489] Add room joining tests --- .../Multiplayer/TestSceneMultiplayer.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 4ea628cb55..92f9c5733f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -22,6 +23,7 @@ using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -85,6 +87,50 @@ namespace osu.Game.Tests.Visual.Multiplayer // used to test the flow of multiplayer from visual tests. } + [Test] + public void TestCreateRoomWithoutPassword() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + } + + [Test] + public void TestJoinRoomWithoutPassword() + { + AddStep("create room", () => + { + API.Queue(new CreateRoomRequest(new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + })); + }); + + AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room", () => InputManager.Key(Key.Enter)); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for join", () => client.Room != null); + } + [Test] public void TestCreateRoomWithPassword() { @@ -105,6 +151,39 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room has password", () => client.APIRoom?.Password.Value == "password"); } + [Test] + public void TestJoinRoomWithPassword() + { + AddStep("create room", () => + { + API.Queue(new CreateRoomRequest(new Room + { + Name = { Value = "Test Room" }, + Password = { Value = "password" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + })); + }); + + AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room", () => InputManager.Key(Key.Enter)); + + DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for join", () => client.Room != null); + } + [Test] public void TestLocalPasswordUpdatedWhenMultiplayerSettingsChange() { From a001e4aa166675a81c8beacc065284aebc158871 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:55:25 +0900 Subject: [PATCH 228/489] Fix web request failing if password is null --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index 0111882d9a..eab508ab7d 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,7 +22,10 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - req.AddParameter("password", Password); + + if (!string.IsNullOrEmpty(Password)) + req.AddParameter("password", Password); + return req; } From 3a4da6b86795c22cabe94028a75f99875574d3a4 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Mon, 19 Jul 2021 20:02:39 +0800 Subject: [PATCH 229/489] use same code style Co-authored-by: frenzibyte --- osu.Game/Localisation/NamedOverlayComponentStrings.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index 0167a1b030..6694d8c008 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -51,10 +51,12 @@ namespace osu.Game.Localisation public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); /// - public static LocalisableString WikiTitle => LayoutStrings.MenuHelpGetWiki; + public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; - /// - public static LocalisableString WikiDescription => PageTitleStrings.MainWikiControllerDefault; + /// + /// "knowledge base" + /// + public static LocalisableString WikiDescription => new TranslatableString(getKey(@"wiki"), @"knowledge base"); private static string getKey(string key) => $"{prefix}:{key}"; } From 81d0a9bd9c4c5acc5050c0dbbaa23eccbfac0b89 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 21:05:36 +0900 Subject: [PATCH 230/489] Fix item ordering --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index dcbac47505..6f84793744 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -16,8 +16,6 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchPlayfield : ScrollingPlayfield { - private readonly BeatmapDifficulty difficulty; - /// /// The width of the playfield. /// The horizontal movement of the catcher is confined in the area of this width. @@ -37,6 +35,8 @@ namespace osu.Game.Rulesets.Catch.UI internal CatcherArea CatcherArea { get; private set; } + private readonly BeatmapDifficulty difficulty; + public CatchPlayfield(BeatmapDifficulty difficulty) { this.difficulty = difficulty; From 0a43e54dfc5d605d45f55cc1327cb95056fdc262 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 21:24:22 +0900 Subject: [PATCH 231/489] Fix request failing due to parameters --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index eab508ab7d..b2d772cac7 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,13 +22,10 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - - if (!string.IsNullOrEmpty(Password)) - req.AddParameter("password", Password); - return req; } - protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}"; + // Todo: Password needs to be specified here rather than via AddParameter() because this is a PUT request. May be a framework bug. + protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}?password={Password}"; } } From fe7aa73aad810d08608dc8722a53de9587c9dc02 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Mon, 19 Jul 2021 20:45:03 +0800 Subject: [PATCH 232/489] Add localisation for BeatmapSetHeader --- osu.Game/Localisation/NamedOverlayComponentStrings.cs | 3 +++ osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index 6694d8c008..f40b97e605 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -18,6 +18,9 @@ namespace osu.Game.Localisation /// public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + /// + public static LocalisableString BeatmapSetTitle => PageTitleStrings.MainBeatmapsetsControllerShow; + /// public static LocalisableString ChangelogTitle => PageTitleStrings.MainChangelogControllerDefault; diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 4b26b02a8e..4fd24d9819 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; using osu.Game.Beatmaps; +using osu.Game.Localisation; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -54,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet { public BeatmapHeaderTitle() { - Title = "beatmap info"; + Title = NamedOverlayComponentStrings.BeatmapSetTitle; IconTexture = "Icons/Hexacons/beatmap"; } } From 892d858d5f4852a65cb8e2af022745a0b12a618d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:23:31 +0900 Subject: [PATCH 233/489] Fix compile error --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 951b00376e..54c01e2c20 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -249,7 +249,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new OsuMenuItem("Create copy", MenuItemType.Standard, () => { - parentScreen?.OpenNewRoom(Room.CreateCopy()); + parentScreen?.OpenNewRoom(Room.DeepClone()); }) }; From 57a99886d5f3fd2da33b9ce8c620dde142338856 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:31:01 +0900 Subject: [PATCH 234/489] Fix password icon not disappearing when no password --- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 26 +++++++++++++++++++ .../Lounge/Components/DrawableRoom.cs | 19 +++++++------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index cec40635f3..6f1b34b078 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -2,8 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -47,5 +51,27 @@ namespace osu.Game.Tests.Visual.Multiplayer } }; } + + [Test] + public void TestEnableAndDisablePassword() + { + DrawableRoom drawableRoom = null; + Room room = null; + + AddStep("create room", () => Child = drawableRoom = new DrawableRoom(room = new Room + { + Name = { Value = "Room with password" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Realtime }, + }) { MatchingFilter = true }); + + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); + + AddStep("set password", () => room.Password.Value = "password"); + AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType().Single().Alpha)); + + AddStep("unset password", () => room.Password.Value = string.Empty); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 54c01e2c20..236408851f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -58,8 +58,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved(canBeNull: true)] private LoungeSubScreen lounge { get; set; } - private Container content; - public readonly Room Room; private SelectionState state; @@ -105,6 +103,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public bool FilteringActive { get; set; } + private PasswordProtectedIcon passwordIcon; + + private readonly Bindable hasPassword = new Bindable(); + public DrawableRoom(Room room) { Room = room; @@ -138,7 +140,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(SELECTION_BORDER_WIDTH), - Child = content = new Container + Child = new Container { RelativeSizeAxes = Axes.Both, Masking = true, @@ -214,15 +216,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, }, }, + passwordIcon = new PasswordProtectedIcon { Alpha = 0 } }, }, }, }; - - if (Room.HasPassword.Value) - { - content.Add(new PasswordProtectedIcon()); - } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -241,6 +239,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components this.FadeInFromZero(transition_duration); else Alpha = 0; + + hasPassword.BindTo(Room.HasPassword); + hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); } public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; @@ -313,7 +314,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - private class PasswordProtectedIcon : CompositeDrawable + public class PasswordProtectedIcon : CompositeDrawable { [BackgroundDependencyLoader] private void load(OsuColour colours) From 7956f73f629641a178f5b0015cdc43de3d2412e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:31:53 +0900 Subject: [PATCH 235/489] Move initial content into step --- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index 6f1b34b078..8c4133418c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -16,40 +16,44 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneRoomStatus : OsuTestScene { - public TestSceneRoomStatus() + [Test] + public void TestMultipleStatuses() { - Child = new FillFlowContainer + AddStep("create rooms", () => { - RelativeSizeAxes = Axes.Both, - Width = 0.5f, - Children = new Drawable[] + Child = new FillFlowContainer { - new DrawableRoom(new Room + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + Children = new Drawable[] { - Name = { Value = "Open - ending in 1 day" }, - Status = { Value = new RoomStatusOpen() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) } - }) { MatchingFilter = true }, - new DrawableRoom(new Room - { - Name = { Value = "Playing - ending in 1 day" }, - Status = { Value = new RoomStatusPlaying() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) } - }) { MatchingFilter = true }, - new DrawableRoom(new Room - { - Name = { Value = "Ended" }, - Status = { Value = new RoomStatusEnded() }, - EndDate = { Value = DateTimeOffset.Now } - }) { MatchingFilter = true }, - new DrawableRoom(new Room - { - Name = { Value = "Open" }, - Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime } - }) { MatchingFilter = true }, - } - }; + new DrawableRoom(new Room + { + Name = { Value = "Open - ending in 1 day" }, + Status = { Value = new RoomStatusOpen() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + }) { MatchingFilter = true }, + new DrawableRoom(new Room + { + Name = { Value = "Playing - ending in 1 day" }, + Status = { Value = new RoomStatusPlaying() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + }) { MatchingFilter = true }, + new DrawableRoom(new Room + { + Name = { Value = "Ended" }, + Status = { Value = new RoomStatusEnded() }, + EndDate = { Value = DateTimeOffset.Now } + }) { MatchingFilter = true }, + new DrawableRoom(new Room + { + Name = { Value = "Open" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Realtime } + }) { MatchingFilter = true }, + } + }; + }); } [Test] From 6a55cb9df0e529a137c6fbd430bffb2676afbe40 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:52:05 +0900 Subject: [PATCH 236/489] Revert unintended change It's a deeper issue with ScreenStack (see: https://github.com/ppy/osu-framework/issues/4619). --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index f35671a761..561fa220c8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -326,7 +326,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. - if (client?.Room == null || !client.IsConnected.Value) + if (client.Room == null || !client.IsConnected.Value) { // room has not been created yet; exit immediately. return base.OnExiting(next); From 8cc1630655e96bf01d9f253bb5526fabed9d9c2b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:15:35 +0900 Subject: [PATCH 237/489] Add initial juice stream editing --- .../Blueprints/Components/EditablePath.cs | 159 ++++++++++++++++++ .../Components/SelectionEditablePath.cs | 90 ++++++++++ .../Edit/Blueprints/Components/VertexPiece.cs | 31 ++++ .../Edit/Blueprints/Components/VertexState.cs | 18 ++ .../JuiceStreamSelectionBlueprint.cs | 56 +++++- 5 files changed, 352 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs new file mode 100644 index 0000000000..a51f18ff33 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -0,0 +1,159 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public abstract class EditablePath : CompositeDrawable + { + public int PathId => path.InvalidationID; + + public IReadOnlyList Vertices => path.Vertices; + + public int VertexCount => path.Vertices.Count; + + protected readonly Func PositionToDistance; + + protected IReadOnlyList VertexStates => vertexStates; + + private readonly JuiceStreamPath path = new JuiceStreamPath(); + + // Invariant: `path.Vertices.Count == vertexStates.Count` + private readonly List vertexStates = new List + { + new VertexState { IsFixed = true } + }; + + private readonly List previousVertexStates = new List(); + + [Resolved(CanBeNull = true)] + [CanBeNull] + private IBeatSnapProvider beatSnapProvider { get; set; } + + protected EditablePath(Func positionToDistance) + { + PositionToDistance = positionToDistance; + + Anchor = Anchor.BottomLeft; + } + + public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject) + { + while (path.Vertices.Count < InternalChildren.Count) + RemoveInternal(InternalChildren[^1]); + + while (InternalChildren.Count < path.Vertices.Count) + AddInternal(new VertexPiece()); + + double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity); + + for (int i = 0; i < VertexCount; i++) + { + var piece = (VertexPiece)InternalChildren[i]; + var vertex = path.Vertices[i]; + piece.Position = new Vector2(vertex.X, (float)(vertex.Distance * distanceToYFactor)); + piece.UpdateFrom(vertexStates[i]); + } + } + + public void InitializeFromHitObject(JuiceStream hitObject) + { + var sliderPath = hitObject.Path; + path.ConvertFromSliderPath(sliderPath); + + // If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices. + if (sliderPath.ControlPoints.Any(p => p.Type.Value != null && p.Type.Value != PathType.Linear)) + { + path.ResampleVertices(hitObject.NestedHitObjects + .Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used. + .Select(h => (h.StartTime - hitObject.StartTime) * hitObject.Velocity)); + } + + vertexStates.Clear(); + vertexStates.AddRange(path.Vertices.Select((_, i) => new VertexState + { + IsFixed = i == 0 + })); + } + + public void UpdateHitObjectFromPath(JuiceStream hitObject) + { + path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY); + + if (beatSnapProvider == null) return; + + double endTime = hitObject.StartTime + path.Distance / hitObject.Velocity; + double snappedEndTime = beatSnapProvider.SnapTime(endTime, hitObject.StartTime); + hitObject.Path.ExpectedDistance.Value = (snappedEndTime - hitObject.StartTime) * hitObject.Velocity; + } + + public Vector2 ToRelativePosition(Vector2 screenSpacePosition) + { + return ToLocalSpace(screenSpacePosition) - new Vector2(0, DrawHeight); + } + + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + + protected void MoveSelectedVertices(double distanceDelta, float xDelta) + { + // Because the vertex list may be reordered due to distance change, the state list must be reordered as well. + previousVertexStates.Clear(); + previousVertexStates.AddRange(vertexStates); + + // We will recreate the path from scratch. Note that `Clear` leaves the first vertex. + int vertexCount = VertexCount; + path.Clear(); + vertexStates.RemoveRange(1, vertexCount - 1); + + for (int i = 1; i < vertexCount; i++) + { + var state = previousVertexStates[i]; + double distance = state.VertexBeforeChange.Distance; + if (state.IsSelected) + distance += distanceDelta; + + int newIndex = path.InsertVertex(Math.Max(0, distance)); + vertexStates.Insert(newIndex, state); + } + + // First, restore positions of the non-selected vertices. + for (int i = 0; i < vertexCount; i++) + { + if (!vertexStates[i].IsSelected && !vertexStates[i].IsFixed) + path.SetVertexPosition(i, vertexStates[i].VertexBeforeChange.X); + } + + // Then, move the selected vertices. + for (int i = 0; i < vertexCount; i++) + { + if (vertexStates[i].IsSelected && !vertexStates[i].IsFixed) + path.SetVertexPosition(i, vertexStates[i].VertexBeforeChange.X + xDelta); + } + + // Finally, correct the position of fixed vertices. + correctFixedVertexPositions(); + } + + private void correctFixedVertexPositions() + { + for (int i = 0; i < VertexCount; i++) + { + if (vertexStates[i].IsFixed) + path.SetVertexPosition(i, vertexStates[i].VertexBeforeChange.X); + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs new file mode 100644 index 0000000000..a6dabca48f --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Input.Events; +using osu.Game.Screens.Edit; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class SelectionEditablePath : EditablePath + { + // To handle when the editor is scrolled while dragging. + private Vector2 dragStartPosition; + + [Resolved(CanBeNull = true)] + [CanBeNull] + private IEditorChangeHandler changeHandler { get; set; } + + public SelectionEditablePath(Func positionToDistance) + : base(positionToDistance) + { + } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => InternalChildren.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); + + protected override bool OnMouseDown(MouseDownEvent e) + { + int index = getMouseTargetVertex(e.ScreenSpaceMouseDownPosition); + + if (index == -1) + return false; + + if (e.ControlPressed) + VertexStates[index].IsSelected = !VertexStates[index].IsSelected; + else if (!VertexStates[index].IsSelected) + selectOnly(index); + + // Don't inhabit right click, to show the context menu + return e.Button != MouseButton.Right; + } + + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Button != MouseButton.Left || getMouseTargetVertex(e.ScreenSpaceMouseDownPosition) == -1) return false; + + dragStartPosition = ToRelativePosition(e.ScreenSpaceMouseDownPosition); + + for (int i = 0; i < VertexCount; i++) + VertexStates[i].VertexBeforeChange = Vertices[i]; + + changeHandler?.BeginChange(); + return true; + } + + protected override void OnDrag(DragEvent e) + { + Vector2 mousePosition = ToLocalSpace(e.ScreenSpaceMousePosition) - new Vector2(0, DrawHeight); + double distanceDelta = PositionToDistance(mousePosition.Y) - PositionToDistance(dragStartPosition.Y); + float xDelta = mousePosition.X - dragStartPosition.X; + MoveSelectedVertices(distanceDelta, xDelta); + } + + protected override void OnDragEnd(DragEndEvent e) + { + changeHandler?.EndChange(); + } + + private int getMouseTargetVertex(Vector2 screenSpacePosition) + { + for (int i = InternalChildren.Count - 1; i >= 0; i--) + { + if (i < VertexCount && InternalChildren[i].ReceivePositionalInputAt(screenSpacePosition)) + return i; + } + + return -1; + } + + private void selectOnly(int index) + { + for (int i = 0; i < VertexCount; i++) + VertexStates[i].IsSelected = i == index; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs new file mode 100644 index 0000000000..5ef86b6074 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class VertexPiece : Circle + { + [Resolved] + private OsuColour osuColour { get; set; } + + public VertexPiece() + { + Anchor = Anchor.BottomLeft; + Origin = Anchor.Centre; + Size = new Vector2(15); + } + + public void UpdateFrom(VertexState state) + { + Colour = state.IsSelected ? osuColour.Yellow.Lighten(1) : osuColour.Yellow; + Alpha = state.IsFixed ? 0.5f : 1; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs new file mode 100644 index 0000000000..55abbf7475 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Objects; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class VertexState + { + public bool IsSelected { get; set; } + + public bool IsFixed { get; set; } + + public JuiceStreamPathVertex VertexBeforeChange { get; set; } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index 0614c4c24d..6b4fd19a8d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; @@ -9,6 +10,7 @@ using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit; using osuTK; namespace osu.Game.Rulesets.Catch.Edit.Blueprints @@ -26,13 +28,24 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private readonly Cached pathCache = new Cached(); + private readonly SelectionEditablePath editablePath; + + private int lastEditablePathId = -1; + + private int lastSliderPathVersion = -1; + + [Resolved(CanBeNull = true)] + [CanBeNull] + private EditorBeatmap editorBeatmap { get; set; } + public JuiceStreamSelectionBlueprint(JuiceStream hitObject) : base(hitObject) { InternalChildren = new Drawable[] { scrollingPath = new ScrollingPath(), - nestedOutlineContainer = new NestedOutlineContainer() + nestedOutlineContainer = new NestedOutlineContainer(), + editablePath = new SelectionEditablePath(positionToDistance) }; } @@ -49,7 +62,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints if (!IsSelected) return; - nestedOutlineContainer.Position = scrollingPath.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + if (editablePath.PathId != lastEditablePathId) + updateHitObjectFromPath(); + + Vector2 startPosition = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + editablePath.Position = nestedOutlineContainer.Position = scrollingPath.Position = startPosition; + + editablePath.UpdateFrom(HitObjectContainer, HitObject); if (pathCache.IsValid) return; @@ -59,10 +78,19 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints pathCache.Validate(); } + protected override void OnSelected() + { + initializeJuiceStreamPath(); + base.OnSelected(); + } + private void onDefaultsApplied(HitObject _) { computeObjectBounds(); pathCache.Invalidate(); + + if (lastSliderPathVersion != HitObject.Path.Version.Value) + initializeJuiceStreamPath(); } private void computeObjectBounds() @@ -81,6 +109,30 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints return new RectangleF(left, top, right - left, bottom - top).Inflate(objectRadius); } + private double positionToDistance(float relativeYPosition) + { + double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime); + return (time - HitObject.StartTime) * HitObject.Velocity; + } + + private void initializeJuiceStreamPath() + { + editablePath.InitializeFromHitObject(HitObject); + + // Record the current ID to update the hit object only when a change is made to the path. + lastEditablePathId = editablePath.PathId; + lastSliderPathVersion = HitObject.Path.Version.Value; + } + + private void updateHitObjectFromPath() + { + editablePath.UpdateHitObjectFromPath(HitObject); + editorBeatmap?.Update(HitObject); + + lastEditablePathId = editablePath.PathId; + lastSliderPathVersion = HitObject.Path.Version.Value; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 01f5258a26a3d1c3314b852a3c114edf068e8702 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:33:03 +0900 Subject: [PATCH 238/489] Add tests of juice stream selection blueprint --- .../CatchSelectionBlueprintTestScene.cs | 50 ++++- .../TestSceneJuiceStreamSelectionBlueprint.cs | 207 ++++++++++++++++-- 2 files changed, 240 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs index dcdc32145b..a458771550 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs @@ -1,10 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; +using osuTK; namespace osu.Game.Rulesets.Catch.Tests.Editor { @@ -14,11 +21,52 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected override Container Content => contentContainer; + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + protected readonly EditorBeatmap EditorBeatmap; + private readonly CatchEditorTestSceneContainer contentContainer; protected CatchSelectionBlueprintTestScene() { - base.Content.Add(contentContainer = new CatchEditorTestSceneContainer()); + EditorBeatmap = new EditorBeatmap(new CatchBeatmap()); + EditorBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = 0; + EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint + { + BeatLength = 100 + }); + + base.Content.Add(new EditorBeatmapDependencyContainer(EditorBeatmap, new BindableBeatDivisor()) + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + EditorBeatmap, + contentContainer = new CatchEditorTestSceneContainer() + }, + }); + } + + protected void AddMouseMoveStep(double time, float x) => AddStep($"move to time={time}, x={x}", () => + { + float y = HitObjectContainer.PositionAtTime(time); + Vector2 pos = HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight)); + InputManager.MoveMouseTo(pos); + }); + + private class EditorBeatmapDependencyContainer : Container + { + [Cached] + private readonly EditorClock editorClock; + + [Cached] + private readonly BindableBeatDivisor beatDivisor; + + public EditorBeatmapDependencyContainer(IBeatmap beatmap, BindableBeatDivisor beatDivisor) + { + editorClock = new EditorClock(beatmap, beatDivisor); + this.beatDivisor = beatDivisor; + } } } } diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 1b96175020..702ec38fb4 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -1,38 +1,213 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Tests.Editor { public class TestSceneJuiceStreamSelectionBlueprint : CatchSelectionBlueprintTestScene { - public TestSceneJuiceStreamSelectionBlueprint() + private JuiceStream hitObject; + + private readonly ManualClock manualClock = new ManualClock(); + + [SetUp] + public void SetUp() => Schedule(() => { - var hitObject = new JuiceStream + EditorBeatmap.Clear(); + Content.Clear(); + + manualClock.CurrentTime = 0; + Content.Clock = new FramedClock(manualClock); + + InputManager.ReleaseButton(MouseButton.Left); + InputManager.ReleaseKey(Key.ShiftLeft); + InputManager.ReleaseKey(Key.ControlLeft); + }); + + [Test] + public void TestBasicComponentLayout() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 100, 200, 100 }; + addBlueprintStep(times, positions); + + for (int i = 0; i < times.Length; i++) + addVertexCheckStep(times.Length, i, times[i], positions[i]); + + AddAssert("correct outline count", () => { - OriginalX = 100, - StartTime = 100, - Path = new SliderPath(PathType.PerfectCurve, new[] + var expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet)); + return this.ChildrenOfType().Count() == expected; + }); + AddAssert("correct vertex piece count", () => + this.ChildrenOfType().Count() == times.Length); + + AddAssert("first vertex is semitransparent", () => + Precision.DefinitelyBigger(1, this.ChildrenOfType().First().Alpha)); + } + + [Test] + public void TestVertexDrag() + { + double[] times = { 100, 400, 700 }; + float[] positions = { 100, 100, 100 }; + addBlueprintStep(times, positions); + + addDragStartStep(times[1], positions[1]); + + AddMouseMoveStep(500, 150); + addVertexCheckStep(3, 1, 500, 150); + + addDragEndStep(); + addDragStartStep(times[2], positions[2]); + + AddMouseMoveStep(300, 50); + addVertexCheckStep(3, 1, 300, 50); + addVertexCheckStep(3, 2, 500, 150); + + AddMouseMoveStep(-100, 100); + addVertexCheckStep(3, 1, times[0], positions[0]); + } + + [Test] + public void TestMultipleDrag() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 100, 100, 100, 100 }; + addBlueprintStep(times, positions); + + AddMouseMoveStep(times[1], positions[1]); + AddStep("press left", () => InputManager.PressButton(MouseButton.Left)); + AddStep("release left", () => InputManager.ReleaseButton(MouseButton.Left)); + AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); + addDragStartStep(times[2], positions[2]); + + AddMouseMoveStep(times[2] - 50, positions[2] - 50); + addVertexCheckStep(4, 1, times[1] - 50, positions[1] - 50); + addVertexCheckStep(4, 2, times[2] - 50, positions[2] - 50); + } + + [Test] + public void TestClampedPositionIsRestored() + { + const double velocity = 0.25; + double[] times = { 100, 500, 700 }; + float[] positions = { 100, 100, 100 }; + addBlueprintStep(times, positions, velocity); + + addDragStartStep(times[1], positions[1]); + + AddMouseMoveStep(times[1], 200); + addVertexCheckStep(3, 1, times[1], 200); + addVertexCheckStep(3, 2, times[2], 150); + + AddMouseMoveStep(times[1], 100); + addVertexCheckStep(3, 1, times[1], 100); + // Stored position is restored. + addVertexCheckStep(3, 2, times[2], positions[2]); + + AddMouseMoveStep(times[1], 300); + addDragEndStep(); + addDragStartStep(times[1], 300); + + AddMouseMoveStep(times[1], 100); + // Position is different because a changed position is committed when the previous drag is ended. + addVertexCheckStep(3, 2, times[2], 250); + } + + [Test] + public void TestScrollWhileDrag() + { + double[] times = { 300, 500 }; + float[] positions = { 100, 100 }; + addBlueprintStep(times, positions); + + addDragStartStep(times[1], positions[1]); + // This mouse move is necessary to start drag and capture the input. + AddMouseMoveStep(times[1], positions[1] + 50); + + AddStep("scroll playfield", () => manualClock.CurrentTime += 200); + AddMouseMoveStep(times[1] + 200, positions[1] + 100); + addVertexCheckStep(2, 1, times[1] + 200, positions[1] + 100); + } + + [Test] + public void TestUpdateFromHitObject() + { + double[] times = { 100, 300 }; + float[] positions = { 200, 200 }; + addBlueprintStep(times, positions); + + AddStep("update hit object path", () => + { + hitObject.Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, - new Vector2(200, 100), + new Vector2(100, 100), new Vector2(0, 200), - }), - }; - var controlPoint = new ControlPointInfo(); - controlPoint.Add(0, new TimingControlPoint - { - BeatLength = 100 + }); + EditorBeatmap.Update(hitObject); }); - hitObject.ApplyDefaults(controlPoint, new BeatmapDifficulty { CircleSize = 0 }); - AddBlueprint(new JuiceStreamSelectionBlueprint(hitObject)); + AddAssert("path is updated", () => getVertices().Count > 2); } + + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => + { + hitObject = new JuiceStream + { + StartTime = time, + X = x, + Path = sliderPath, + }; + EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity; + EditorBeatmap.Add(hitObject); + EditorBeatmap.Update(hitObject); + Assert.That(hitObject.Velocity, Is.EqualTo(velocity)); + AddBlueprint(new JuiceStreamSelectionBlueprint(hitObject)); + }); + + private void addBlueprintStep(double[] times, float[] positions, double velocity = 0.5) + { + var path = new JuiceStreamPath(); + for (int i = 1; i < times.Length; i++) + path.Add((times[i] - times[0]) * velocity, positions[i] - positions[0]); + + var sliderPath = new SliderPath(); + path.ConvertToSliderPath(sliderPath, 0); + addBlueprintStep(times[0], positions[0], sliderPath, velocity); + } + + private IReadOnlyList getVertices() => this.ChildrenOfType().Single().Vertices; + + private void addVertexCheckStep(int count, int index, double time, float x) => AddAssert($"vertex {index} of {count} at {time}, {x}", () => + { + double expectedDistance = (time - hitObject.StartTime) * hitObject.Velocity; + float expectedX = x - hitObject.OriginalX; + var vertices = getVertices(); + return vertices.Count == count && + Precision.AlmostEquals(vertices[index].Distance, expectedDistance, 1e-3) && + Precision.AlmostEquals(vertices[index].X, expectedX); + }); + + private void addDragStartStep(double time, float x) + { + AddMouseMoveStep(time, x); + AddStep("start dragging", () => InputManager.PressButton(MouseButton.Left)); + } + + private void addDragEndStep() => AddStep("end dragging", () => InputManager.ReleaseButton(MouseButton.Left)); } } From 08f8d4e65e2144d39c1fffd4d6a56e020e5c153b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:43:28 +0900 Subject: [PATCH 239/489] Implement vertex addition in juice stream selection blueprint --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 29 +++++++++++++++ .../Blueprints/Components/EditablePath.cs | 13 +++++++ .../Components/SelectionEditablePath.cs | 7 ++++ .../JuiceStreamSelectionBlueprint.cs | 36 +++++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 702ec38fb4..7a8db79fa3 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -164,6 +164,24 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddAssert("path is updated", () => getVertices().Count > 2); } + [Test] + public void TestAddVertex() + { + double[] times = { 100, 700 }; + float[] positions = { 200, 200 }; + addBlueprintStep(times, positions, 0.2); + + addAddVertexSteps(500, 150); + addVertexCheckStep(3, 1, 500, 150); + + addAddVertexSteps(90, 220); + addVertexCheckStep(4, 1, times[0], positions[0]); + + addAddVertexSteps(750, 180); + addVertexCheckStep(5, 4, 750, 180); + AddAssert("duration is changed", () => Precision.AlmostEquals(hitObject.Duration, 800 - times[0], 1e-3)); + } + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => { hitObject = new JuiceStream @@ -209,5 +227,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor } private void addDragEndStep() => AddStep("end dragging", () => InputManager.ReleaseButton(MouseButton.Left)); + + private void addAddVertexSteps(double time, float x) + { + AddMouseMoveStep(time, x); + AddStep("add vertex", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Click(MouseButton.Left); + InputManager.ReleaseKey(Key.ControlLeft); + }); + } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index a51f18ff33..4768689178 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -107,6 +108,18 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + protected int AddVertex(double distance, float x) + { + int index = path.InsertVertex(distance); + path.SetVertexPosition(index, x); + vertexStates.Insert(index, new VertexState()); + + correctFixedVertexPositions(); + + Debug.Assert(vertexStates.Count == VertexCount); + return index; + } + protected void MoveSelectedVertices(double distanceDelta, float xDelta) { // Because the vertex list may be reordered due to distance change, the state list must be reordered as well. diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index a6dabca48f..7152f6108f 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -26,6 +26,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { } + public void AddVertex(Vector2 relativePosition) + { + double distance = Math.Max(0, PositionToDistance(relativePosition.Y)); + int index = AddVertex(distance, relativePosition.X); + selectOnly(index); + } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => InternalChildren.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index 6b4fd19a8d..defb5eae8b 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -1,17 +1,22 @@ // 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 JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit.Blueprints { @@ -19,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { public override Quad SelectionQuad => HitObjectContainer.ToScreenSpace(getBoundingBox().Offset(new Vector2(0, HitObjectContainer.DrawHeight))); + public override MenuItem[] ContextMenuItems => getContextMenuItems().ToArray(); + private float minNestedX; private float maxNestedX; @@ -34,6 +41,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private int lastSliderPathVersion = -1; + private Vector2 rightMouseDownPosition; + [Resolved(CanBeNull = true)] [CanBeNull] private EditorBeatmap editorBeatmap { get; set; } @@ -84,6 +93,25 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints base.OnSelected(); } + protected override bool OnMouseDown(MouseDownEvent e) + { + if (!IsSelected) return base.OnMouseDown(e); + + switch (e.Button) + { + case MouseButton.Left when e.ControlPressed: + editablePath.AddVertex(editablePath.ToRelativePosition(e.ScreenSpaceMouseDownPosition)); + return true; + + case MouseButton.Right: + // Record the mouse position to be used in the "add vertex" action. + rightMouseDownPosition = editablePath.ToRelativePosition(e.ScreenSpaceMouseDownPosition); + break; + } + + return base.OnMouseDown(e); + } + private void onDefaultsApplied(HitObject _) { computeObjectBounds(); @@ -133,6 +161,14 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints lastSliderPathVersion = HitObject.Path.Version.Value; } + private IEnumerable getContextMenuItems() + { + yield return new OsuMenuItem("Add vertex", MenuItemType.Standard, () => + { + editablePath.AddVertex(rightMouseDownPosition); + }); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 85864587040d8ebb3955d26405b662a059469f08 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:46:32 +0900 Subject: [PATCH 240/489] Implement vertex deletion in juice stream selection blueprint --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 29 +++++++++++++++++ .../Blueprints/Components/EditablePath.cs | 18 +++++++++++ .../Components/SelectionEditablePath.cs | 31 ++++++++++++++++++- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 7a8db79fa3..2c1b95a726 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -182,6 +182,24 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddAssert("duration is changed", () => Precision.AlmostEquals(hitObject.Duration, 800 - times[0], 1e-3)); } + [Test] + public void TestDeleteVertex() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 100, 200, 150 }; + addBlueprintStep(times, positions); + + addDeleteVertexSteps(times[1], positions[1]); + addVertexCheckStep(2, 1, times[2], positions[2]); + + // The first vertex cannot be deleted. + addDeleteVertexSteps(times[0], positions[0]); + addVertexCheckStep(2, 0, times[0], positions[0]); + + addDeleteVertexSteps(times[2], positions[2]); + addVertexCheckStep(1, 0, times[0], positions[0]); + } + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => { hitObject = new JuiceStream @@ -238,5 +256,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor InputManager.ReleaseKey(Key.ControlLeft); }); } + + private void addDeleteVertexSteps(double time, float x) + { + AddMouseMoveStep(time, x); + AddStep("delete vertex", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Click(MouseButton.Left); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 4768689178..8aaeef045f 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -120,6 +120,24 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components return index; } + protected bool RemoveVertex(int index) + { + if (index < 0 || index >= path.Vertices.Count) + return false; + + if (vertexStates[index].IsFixed) + return false; + + path.RemoveVertices((_, i) => i == index); + + vertexStates.RemoveAt(index); + if (vertexStates.Count == 0) + vertexStates.Add(new VertexState()); + + Debug.Assert(vertexStates.Count == VertexCount); + return true; + } + protected void MoveSelectedVertices(double distanceDelta, float xDelta) { // Because the vertex list may be reordered due to distance change, the state list must be reordered as well. diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 7152f6108f..3acb7f43a6 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -2,18 +2,24 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Edit; using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { - public class SelectionEditablePath : EditablePath + public class SelectionEditablePath : EditablePath, IHasContextMenu { + public MenuItem[] ContextMenuItems => getContextMenuItems().ToArray(); + // To handle when the editor is scrolled while dragging. private Vector2 dragStartPosition; @@ -42,6 +48,12 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components if (index == -1) return false; + if (e.Button == MouseButton.Left && e.ShiftPressed) + { + RemoveVertex(index); + return true; + } + if (e.ControlPressed) VertexStates[index].IsSelected = !VertexStates[index].IsSelected; else if (!VertexStates[index].IsSelected) @@ -88,10 +100,27 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components return -1; } + private IEnumerable getContextMenuItems() + { + int selectedCount = VertexStates.Count(state => state.IsSelected); + + if (selectedCount != 0) + yield return new OsuMenuItem($"Delete selected {(selectedCount == 1 ? "vertex" : $"{selectedCount} vertices")}", MenuItemType.Destructive, deleteSelectedVertices); + } + private void selectOnly(int index) { for (int i = 0; i < VertexCount; i++) VertexStates[i].IsSelected = i == index; } + + private void deleteSelectedVertices() + { + for (int i = VertexCount - 1; i >= 0; i--) + { + if (VertexStates[i].IsSelected) + RemoveVertex(i); + } + } } } From 9db5847344bf28319547b48ebc602bbb26cc89bf Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:49:03 +0900 Subject: [PATCH 241/489] Add test that a slider path is resampled when the path is edited --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 2c1b95a726..f5ef5c5e18 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -200,6 +200,21 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor addVertexCheckStep(1, 0, times[0], positions[0]); } + [Test] + public void TestVertexResampling() + { + addBlueprintStep(100, 100, new SliderPath(PathType.PerfectCurve, new[] + { + Vector2.Zero, + new Vector2(100, 100), + new Vector2(50, 200), + }), 0.5); + AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count); + AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.PerfectCurve); + addAddVertexSteps(150, 150); + AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.Linear); + } + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => { hitObject = new JuiceStream From 9ea1f5900a78ca02bdb11efa37fd3a87b6f71889 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 01:05:36 +0900 Subject: [PATCH 242/489] Don't consider version suffixes when checking for updates This is just to make life easier for me with deploys for now. --- osu.Game/Updater/SimpleUpdateManager.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs index 50572a7867..e0409e34df 100644 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ b/osu.Game/Updater/SimpleUpdateManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; using osu.Framework; @@ -41,11 +42,16 @@ namespace osu.Game.Updater var latest = releases.ResponseObject; - if (latest.TagName != version) + // avoid any discrepancies due to build suffixes for now. + // eventually we will want to support release streams and consider these. + version = version.Split('-').First(); + var latestTagName = latest.TagName.Split('-').First(); + + if (latestTagName != version) { Notifications.Post(new SimpleNotification { - Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n" + Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n" + "Click here to download the new version, which can be installed over the top of your existing installation", Icon = FontAwesome.Solid.Upload, Activated = () => From ccc782ea7eb718aa46248244590760aecd29954c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:24:02 +0300 Subject: [PATCH 243/489] Add `description` to key names of description strings --- .../Localisation/NamedOverlayComponentStrings.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index f40b97e605..b09e128a2b 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Localisation /// /// "browse for new beatmaps" /// - public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing_description"), @"browse for new beatmaps"); /// public static LocalisableString BeatmapSetTitle => PageTitleStrings.MainBeatmapsetsControllerShow; @@ -27,7 +27,7 @@ namespace osu.Game.Localisation /// /// "track recent dev updates in the osu! ecosystem" /// - public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog_description"), @"track recent dev updates in the osu! ecosystem"); /// public static LocalisableString DashboardTitle => PageTitleStrings.MainHomeControllerIndex; @@ -35,7 +35,7 @@ namespace osu.Game.Localisation /// /// "view your friends and other information" /// - public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard_description"), @"view your friends and other information"); /// public static LocalisableString RankingsTitle => PageTitleStrings.MainRankingControllerDefault; @@ -43,7 +43,7 @@ namespace osu.Game.Localisation /// /// "find out who's the best right now" /// - public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings_description"), @"find out who's the best right now"); /// public static LocalisableString NewsTitle => PageTitleStrings.MainNewsControllerDefault; @@ -51,7 +51,7 @@ namespace osu.Game.Localisation /// /// "get up-to-date on community happenings" /// - public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); + public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news_description"), @"get up-to-date on community happenings"); /// public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; @@ -59,7 +59,7 @@ namespace osu.Game.Localisation /// /// "knowledge base" /// - public static LocalisableString WikiDescription => new TranslatableString(getKey(@"wiki"), @"knowledge base"); + public static LocalisableString WikiDescription => new TranslatableString(getKey(@"wiki_description"), @"knowledge base"); private static string getKey(string key) => $"{prefix}:{key}"; } From 944bf2c4f946eeed89ea7e1b29cf654eab1dbbc3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:25:15 +0300 Subject: [PATCH 244/489] Fix incorrect property xmldoc inherited --- osu.Game/Localisation/NamedOverlayComponentStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index b09e128a2b..f160ee57c7 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -53,7 +53,7 @@ namespace osu.Game.Localisation /// public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news_description"), @"get up-to-date on community happenings"); - /// + /// public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; /// From 456f4e6f1fce118405e0947f73705e90d1c30306 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:29:35 +0300 Subject: [PATCH 245/489] Move `RankingsScope` to own file and fix mapper strings --- .../Rankings/RankingsOverlayHeader.cs | 36 ---------------- osu.Game/Overlays/Rankings/RankingsScope.cs | 42 +++++++++++++++++++ 2 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 osu.Game/Overlays/Rankings/RankingsScope.cs diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index b6c16d398d..c0f77049de 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -1,13 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Graphics; using osu.Framework.Bindables; -using osu.Framework.Localisation; using osu.Game.Localisation; using osu.Game.Rulesets; -using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings @@ -39,37 +36,4 @@ namespace osu.Game.Overlays.Rankings } } } - - [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] - public enum RankingsScope - { - Performance, - Spotlights, - Score, - Country - } - - public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(RankingsScope value) - { - switch (value) - { - case RankingsScope.Performance: - return LayoutStrings.MenuRankingsIndex; - - case RankingsScope.Spotlights: - return LayoutStrings.MenuRankingsCharts; - - case RankingsScope.Score: - return LayoutStrings.MenuRankingsScore; - - case RankingsScope.Country: - return LayoutStrings.MenuRankingsCountry; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Rankings/RankingsScope.cs b/osu.Game/Overlays/Rankings/RankingsScope.cs new file mode 100644 index 0000000000..684408d3a2 --- /dev/null +++ b/osu.Game/Overlays/Rankings/RankingsScope.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + +namespace osu.Game.Overlays.Rankings +{ + [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] + public enum RankingsScope + { + Performance, + Spotlights, + Score, + Country + } + + public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsScope value) + { + switch (value) + { + case RankingsScope.Performance: + return RankingsStrings.TypePerformance; + + case RankingsScope.Spotlights: + return RankingsStrings.TypeCharts; + + case RankingsScope.Score: + return RankingsStrings.TypeScore; + + case RankingsScope.Country: + return RankingsStrings.TypeCountry; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } +} From 51b056bf664332c963a05494071dab530be774ce Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:36:04 +0300 Subject: [PATCH 246/489] Fix `WikiHeader` setting `string.Empty` rather than `null` on breadcrumb That's supposed to be fixed already in the base PR, but somehow it continued to exist here. --- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 29ab385cd8..b57bffb3d8 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = string.Empty; + Current.Value = null; TabControl.AddItem(IndexPageString); From 6a8c16d3ef9e215a2021b801f4dd4471dbb031f6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 03:29:32 +0300 Subject: [PATCH 247/489] Fix news/wiki tests checking against incorrect constant string --- osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs | 4 ++-- osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs index 78288bf6e4..994c4fce53 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestControl() { - AddAssert("Front page selected", () => header.Current.Value == "frontpage"); + AddAssert("Front page selected", () => header.Current.Value == NewsHeader.FrontPageString); AddAssert("1 tab total", () => header.TabCount == 1); AddStep("Set article 1", () => header.SetArticle("1")); @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("2 tabs total", () => header.TabCount == 2); AddStep("Set front page", () => header.SetFrontPage()); - AddAssert("Front page selected", () => header.Current.Value == "frontpage"); + AddAssert("Front page selected", () => header.Current.Value == NewsHeader.FrontPageString); AddAssert("1 tab total", () => header.TabCount == 1); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index e7e6030c66..08e61d19f4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestWikiHeader() { - AddAssert("Current is index", () => checkCurrent("index")); + AddAssert("Current is index", () => checkCurrent(WikiHeader.IndexPageString)); AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage { @@ -54,8 +54,8 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Current is welcome", () => checkCurrent("Welcome")); AddAssert("Check breadcrumb", checkBreadcrumb); - AddStep("Change current to index", () => header.Current.Value = "index"); - AddAssert("Current is index", () => checkCurrent("index")); + AddStep("Change current to index", () => header.Current.Value = WikiHeader.IndexPageString); + AddAssert("Current is index", () => checkCurrent(WikiHeader.IndexPageString)); AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage { @@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Check breadcrumb", checkBreadcrumb); } - private bool checkCurrent(string expectedCurrent) => header.Current.Value == expectedCurrent; + private bool checkCurrent(LocalisableString expectedCurrent) => header.Current.Value == expectedCurrent; private bool checkBreadcrumb() { From a387d8df740d28acb4d5f285913537711e2db912 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 20 Jul 2021 10:30:40 +0800 Subject: [PATCH 248/489] Use `BeatSyncClock` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 918b9b1c94..f682860498 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -374,8 +374,7 @@ namespace osu.Game.Rulesets.Osu.Mods int timeSignature = (int)timingPoint.TimeSignature; // play metronome from one measure before the first object. - // TODO: Use BeatSyncClock from https://github.com/ppy/osu/pull/13894. - if (Clock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) return; sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; From 1c6a13fca79aacd28a269effe010b4332a064264 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 20 Jul 2021 10:31:19 +0800 Subject: [PATCH 249/489] Disallow mistimed event firing --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index f682860498..e21d1da009 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -353,6 +353,7 @@ namespace osu.Game.Rulesets.Osu.Mods public TargetBeatContainer(double firstHitTime) { this.firstHitTime = firstHitTime; + AllowMistimedEventFiring = false; Divisor = 1; } From 662822a40c295a627d428423156ef3b17bbcc857 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 14:46:22 +0900 Subject: [PATCH 250/489] Avoid showing time of play on results screen when autoplay Closes https://github.com/ppy/osu/issues/13940. --- .../TestScenePlaylistsResultsScreen.cs | 9 +- .../TestSceneExpandedPanelMiddleContent.cs | 27 +- .../BeatmapSet/Scores/TopScoreUserSection.cs | 5 +- .../Sections/Ranks/DrawableProfileScore.cs | 5 +- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 4 +- .../Expanded/ExpandedPanelMiddleContent.cs | 273 +++++++++--------- 7 files changed, 183 insertions(+), 142 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 61d49e4018..95dab61d7b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Net; using JetBrains.Annotations; @@ -226,11 +227,13 @@ namespace osu.Game.Tests.Visual.Playlists private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore) { + Debug.Assert(userScore.Date != null); + var multiplayerUserScore = new MultiplayerScore { ID = (int)(userScore.OnlineScoreID ?? currentScoreId++), Accuracy = userScore.Accuracy, - EndedAt = userScore.Date, + EndedAt = userScore.Date.Value, Passed = userScore.Passed, Rank = userScore.Rank, Position = 200, @@ -251,7 +254,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date, + EndedAt = userScore.Date.Value, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, @@ -269,7 +272,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date, + EndedAt = userScore.Date.Value, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 591095252f..46450cebc9 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -12,6 +12,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -50,9 +51,33 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("mapped by text not present", () => this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text.ToString(), "mapped", "by"))); + + AddAssert("play time displayed", () => this.ChildrenOfType().Any()); } - private void showPanel(ScoreInfo score) => Child = new ExpandedPanelMiddleContentContainer(score); + [Test] + public void TestWithNullDate() + { + AddStep("show autoplay score", () => + { + var ruleset = new OsuRuleset(); + + var mods = new Mod[] { ruleset.GetAutoplayMod() }; + var beatmap = createTestBeatmap(null); + + showPanel(new TestScoreInfo(ruleset.RulesetInfo) + { + Mods = mods, + Beatmap = beatmap, + Date = null, + }); + }); + + AddAssert("play time not displayed", () => !this.ChildrenOfType().Any()); + } + + private void showPanel(ScoreInfo score) => + Child = new ExpandedPanelMiddleContentContainer(score); private BeatmapInfo createTestBeatmap(User author) { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 736366fb5c..324d82b9c2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -136,9 +137,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { set { + Debug.Assert(value.Date != null); + avatar.User = value.User; flag.Country = value.User.Country; - achievedOn.Date = value.Date; + achievedOn.Date = value.Date.Value; usernameText.Clear(); usernameText.AddUserLink(value.User); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 713303285a..ec02273a99 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.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.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -44,6 +45,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [BackgroundDependencyLoader] private void load() { + Debug.Assert(Score.Date != null); + AddInternal(new ProfileItemContainer { Children = new Drawable[] @@ -92,7 +95,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Colour = colours.Yellow }, - new DrawableDate(Score.Date, 12) + new DrawableDate(Score.Date.Value, 12) { Colour = colourProvider.Foreground1 } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 288552879c..0a405a9933 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -59,7 +59,7 @@ namespace osu.Game.Scoring.Legacy sw.Write((int)score.ScoreInfo.Ruleset.CreateInstance().ConvertToLegacyMods(score.ScoreInfo.Mods)); sw.Write(getHpGraphFormatted()); - sw.Write(score.ScoreInfo.Date.DateTime); + sw.Write((score.ScoreInfo.Date ?? DateTimeOffset.Now).DateTime); sw.WriteByteArray(createReplayData()); sw.Write((long)0); writeModSpecificData(score.ScoreInfo, sw); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a0c4d5a026..d2df0058d8 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; @@ -155,7 +156,8 @@ namespace osu.Game.Scoring public long? OnlineScoreID { get; set; } [JsonIgnore] - public DateTimeOffset Date { get; set; } + [CanBeNull] // may be null in cases of autoplay. + public DateTimeOffset? Date { get; set; } [JsonProperty("statistics")] public Dictionary Statistics = new Dictionary(); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 4d3f7a4184..dd053c11f9 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.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 System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -79,162 +80,155 @@ namespace osu.Game.Screens.Ranking.Expanded var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).Result; - InternalChildren = new Drawable[] + AddInternal(new FillFlowContainer { - new FillFlowContainer + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(20), - Children = new Drawable[] + new FillFlowContainer { - new FillFlowContainer + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] + new OsuSpriteText { - new OsuSpriteText + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), + Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, + }, + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 40 }, + RelativeSizeAxes = Axes.X, + Height = 230, + Child = new AccuracyCircle(score, withFlair) { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), - Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), - MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, - }, - new OsuSpriteText + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + } + }, + scoreCounter = new TotalScoreCounter + { + Margin = new MarginPadding { Top = 0, Bottom = 5 }, + Current = { Value = 0 }, + Alpha = 0, + AlwaysPresent = true + }, + starAndModDisplay = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5, 0), + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), - Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), - MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, - }, - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 40 }, - RelativeSizeAxes = Axes.X, - Height = 230, - Child = new AccuracyCircle(score, withFlair) + new StarRatingDisplay(starDifficulty) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - } - }, - scoreCounter = new TotalScoreCounter + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + } + }, + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Margin = new MarginPadding { Top = 0, Bottom = 5 }, - Current = { Value = 0 }, - Alpha = 0, - AlwaysPresent = true - }, - starAndModDisplay = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5, 0), - Children = new Drawable[] + new OsuSpriteText { - new StarRatingDisplay(starDifficulty) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = beatmap.Version, + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + }, + new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) { - new OsuSpriteText + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + }.With(t => + { + if (!string.IsNullOrEmpty(creator)) { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = beatmap.Version, - Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), - }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - }.With(t => - { - if (!string.IsNullOrEmpty(creator)) - { - t.AddText("mapped by "); - t.AddText(creator, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); - } - }) - } - }, - } - }, - new FillFlowContainer + t.AddText("mapped by "); + t.AddText(creator, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); + } + }) + } + }, + } + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - Children = new Drawable[] + new GridContainer { - new GridContainer + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { topStatistics.Cast().ToArray() }, + RowDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { topStatistics.Cast().ToArray() }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } - }, - new GridContainer + new Dimension(GridSizeMode.AutoSize), + } + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { bottomStatistics.Where(s => s.Result <= HitResult.Perfect).ToArray() }, + RowDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { bottomStatistics.Where(s => s.Result <= HitResult.Perfect).ToArray() }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } - }, - new GridContainer + new Dimension(GridSizeMode.AutoSize), + } + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { bottomStatistics.Where(s => s.Result > HitResult.Perfect).ToArray() }, + RowDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { bottomStatistics.Where(s => s.Result > HitResult.Perfect).ToArray() }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } + new Dimension(GridSizeMode.AutoSize), } } } } - }, - new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}" } - }; + }); + + if (score.Date != null) + AddInternal(new PlayedOnText(score.Date.Value)); if (score.Mods.Any()) { @@ -276,5 +270,16 @@ namespace osu.Game.Screens.Ranking.Expanded FinishTransforms(true); }); } + + public class PlayedOnText : OsuSpriteText + { + public PlayedOnText(DateTimeOffset time) + { + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold); + Text = $"Played on {time.ToLocalTime():d MMMM yyyy HH:mm}"; + } + } } } From fe414b942f69230c473398b8ee2e199c06449a84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 15:44:32 +0900 Subject: [PATCH 251/489] Ensure online play subscreen is loaded before forwarding `OnExiting` Closes https://github.com/ppy/osu-framework/issues/4619 (actually not a framework issue; the framework correctly guards against this scenario, see https://github.com/ppy/osu-framework/blob/4e29504384bb78bbccc0d492de58124f17270eb2/osu.Framework/Screens/ScreenStack.cs#L277). Added the assertions to be very explicit about the nested stack's state at this point. Both of those events can only be triggered if the stack has a loaded screen (as far as I can tell), making this check unnecessary in those cases. --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index ceee002c6e..3aef05c26e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.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.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -222,7 +223,9 @@ namespace osu.Game.Screens.OnlinePlay this.FadeIn(250); this.ScaleTo(1, 250, Easing.OutSine); - screenStack.CurrentScreen?.OnResuming(last); + Debug.Assert(screenStack.CurrentScreen != null); + screenStack.CurrentScreen.OnResuming(last); + base.OnResuming(last); UpdatePollingRate(isIdle.Value); @@ -233,14 +236,16 @@ namespace osu.Game.Screens.OnlinePlay this.ScaleTo(1.1f, 250, Easing.InSine); this.FadeOut(250); - screenStack.CurrentScreen?.OnSuspending(next); + Debug.Assert(screenStack.CurrentScreen != null); + screenStack.CurrentScreen.OnSuspending(next); UpdatePollingRate(isIdle.Value); } public override bool OnExiting(IScreen next) { - if (screenStack.CurrentScreen?.OnExiting(next) == true) + var subScreen = screenStack.CurrentScreen as Drawable; + if (subScreen?.IsLoaded == true && screenStack.CurrentScreen.OnExiting(next)) return true; RoomManager.PartRoom(); From a1001542f34f20727df86b244184afd68141ce49 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 10:28:24 +0300 Subject: [PATCH 252/489] Update outdated test cases --- .../TestSceneLegacyBeatmapSkin.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 13df5070d0..a009c560cb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -86,9 +86,8 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public void TestLegacyOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); - base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); - + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); + ConfigureTest(useBeatmapSkin, true, userHasCustomColours); assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", TestBeatmapSkin.Colours, (i, obj) => i + 1 + obj.LegacyBeatmapComboOffset); @@ -98,9 +97,8 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false)] public void TestLegacyOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); - base.TestBeatmapComboColoursOverride(useBeatmapSkin); - + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); + ConfigureTest(useBeatmapSkin, false, true); assertCorrectObjectComboColours("is user skin colours without legacy offsets applied", TestSkin.Colours, (i, _) => i + 1); From 4113eae6ade3cc1d55819d0d19cde3fda4458877 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 16:23:48 +0900 Subject: [PATCH 253/489] Add test coverage of fail scenario --- .../Multiplayer/TestSceneMultiplayer.cs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 92f9c5733f..f39455dc85 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -104,6 +104,36 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } + [Test] + public void TestExitMidJoin() + { + Room room = null; + + AddStep("create room", () => + { + room = new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }; + }); + + AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room and immediately exit", () => + { + multiplayerScreen.ChildrenOfType().Single().Open(room); + Schedule(() => Stack.CurrentScreen.Exit()); + }); + } + [Test] public void TestJoinRoomWithoutPassword() { @@ -179,9 +209,6 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); - - AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddUntilStep("wait for join", () => client.Room != null); } [Test] From d87ea741edb57bae885f714ec3e81158ce1e4999 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 16:38:39 +0900 Subject: [PATCH 254/489] Fix `OnlinePlayScreen` sub-screens not loaded asynchronously --- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index ceee002c6e..a4bcad5776 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -30,17 +30,19 @@ namespace osu.Game.Screens.OnlinePlay [Cached] public abstract class OnlinePlayScreen : OsuScreen, IHasSubScreenStack { - public override bool CursorVisible => (screenStack.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; + public override bool CursorVisible => (screenStack?.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; // this is required due to PlayerLoader eventually being pushed to the main stack // while leases may be taken out by a subscreen. public override bool DisallowExternalBeatmapRulesetChanges => true; - private readonly MultiplayerWaveContainer waves; + private MultiplayerWaveContainer waves; - private readonly OsuButton createButton; - private readonly LoungeSubScreen loungeSubScreen; - private readonly ScreenStack screenStack; + private OsuButton createButton; + + private ScreenStack screenStack; + + private LoungeSubScreen loungeSubScreen; private readonly IBindable isIdle = new BindableBool(); @@ -54,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); [Cached] - private OngoingOperationTracker ongoingOperationTracker { get; set; } + private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); [Resolved(CanBeNull = true)] private MusicController music { get; set; } @@ -65,11 +67,14 @@ namespace osu.Game.Screens.OnlinePlay [Resolved] protected IAPIProvider API { get; private set; } + [Resolved(CanBeNull = true)] + private IdleTracker idleTracker { get; set; } + [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } - private readonly Drawable header; - private readonly Drawable headerBackground; + private Drawable header; + private Drawable headerBackground; protected OnlinePlayScreen() { @@ -78,6 +83,14 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both; Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING }; + RoomManager = CreateRoomManager(); + } + + private readonly IBindable apiState = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { var backgroundColour = Color4Extensions.FromHex(@"3e3a44"); InternalChild = waves = new MultiplayerWaveContainer @@ -144,27 +157,14 @@ namespace osu.Game.Screens.OnlinePlay }; button.Action = () => OpenNewRoom(); }), - RoomManager = CreateRoomManager(), - ongoingOperationTracker = new OngoingOperationTracker() + RoomManager, + ongoingOperationTracker, } }; - screenStack.ScreenPushed += screenPushed; - screenStack.ScreenExited += screenExited; - - screenStack.Push(loungeSubScreen = CreateLounge()); - } - - private readonly IBindable apiState = new Bindable(); - - [BackgroundDependencyLoader(true)] - private void load(IdleTracker idleTracker) - { - apiState.BindTo(API.State); - apiState.BindValueChanged(onlineStateChanged, true); - - if (idleTracker != null) - isIdle.BindTo(idleTracker.IsIdle); + // a lot of the functionality in this class depends on loungeSubScreen being in a ready to go state. + // as such, we intentionally load this inline so it is ready alongside this screen. + LoadComponent(loungeSubScreen = CreateLounge()); } private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => @@ -179,7 +179,20 @@ namespace osu.Game.Screens.OnlinePlay protected override void LoadComplete() { base.LoadComplete(); - isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true); + + screenStack.ScreenPushed += screenPushed; + screenStack.ScreenExited += screenExited; + + screenStack.Push(loungeSubScreen); + + apiState.BindTo(API.State); + apiState.BindValueChanged(onlineStateChanged, true); + + if (idleTracker != null) + { + isIdle.BindTo(idleTracker.IsIdle); + isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true); + } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From 26cc4af87c03fe513f4b5d31e27fb61db7245519 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 16:44:51 +0900 Subject: [PATCH 255/489] Revert unintended changes --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index f39455dc85..36dd9c2de3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -209,6 +209,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for join", () => client.Room != null); } [Test] From 1a8ab77f21eaf4a22c12c586b205b418a3259b7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:03:29 +0900 Subject: [PATCH 256/489] Revert nullability change --- .../Visual/Playlists/TestScenePlaylistsResultsScreen.cs | 9 +++------ .../Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 5 +---- .../Profile/Sections/Ranks/DrawableProfileScore.cs | 5 +---- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 4 +--- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 95dab61d7b..61d49e4018 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Net; using JetBrains.Annotations; @@ -227,13 +226,11 @@ namespace osu.Game.Tests.Visual.Playlists private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore) { - Debug.Assert(userScore.Date != null); - var multiplayerUserScore = new MultiplayerScore { ID = (int)(userScore.OnlineScoreID ?? currentScoreId++), Accuracy = userScore.Accuracy, - EndedAt = userScore.Date.Value, + EndedAt = userScore.Date, Passed = userScore.Passed, Rank = userScore.Rank, Position = 200, @@ -254,7 +251,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date.Value, + EndedAt = userScore.Date, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, @@ -272,7 +269,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date.Value, + EndedAt = userScore.Date, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 324d82b9c2..736366fb5c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -137,11 +136,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { set { - Debug.Assert(value.Date != null); - avatar.User = value.User; flag.Country = value.User.Country; - achievedOn.Date = value.Date.Value; + achievedOn.Date = value.Date; usernameText.Clear(); usernameText.AddUserLink(value.User); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index ec02273a99..713303285a 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -45,8 +44,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [BackgroundDependencyLoader] private void load() { - Debug.Assert(Score.Date != null); - AddInternal(new ProfileItemContainer { Children = new Drawable[] @@ -95,7 +92,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Colour = colours.Yellow }, - new DrawableDate(Score.Date.Value, 12) + new DrawableDate(Score.Date, 12) { Colour = colourProvider.Foreground1 } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 0a405a9933..288552879c 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -59,7 +59,7 @@ namespace osu.Game.Scoring.Legacy sw.Write((int)score.ScoreInfo.Ruleset.CreateInstance().ConvertToLegacyMods(score.ScoreInfo.Mods)); sw.Write(getHpGraphFormatted()); - sw.Write((score.ScoreInfo.Date ?? DateTimeOffset.Now).DateTime); + sw.Write(score.ScoreInfo.Date.DateTime); sw.WriteByteArray(createReplayData()); sw.Write((long)0); writeModSpecificData(score.ScoreInfo, sw); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index d2df0058d8..a0c4d5a026 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; @@ -156,8 +155,7 @@ namespace osu.Game.Scoring public long? OnlineScoreID { get; set; } [JsonIgnore] - [CanBeNull] // may be null in cases of autoplay. - public DateTimeOffset? Date { get; set; } + public DateTimeOffset Date { get; set; } [JsonProperty("statistics")] public Dictionary Statistics = new Dictionary(); From 8e1f8c28bdd147191d38af99cf536e09690c6015 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:05:08 +0900 Subject: [PATCH 257/489] Use `default` value to denote no play date, rather than `null` --- .../Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs | 4 ++-- .../Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 46450cebc9..10fcd3cffd 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Ranking } [Test] - public void TestWithNullDate() + public void TestWithDefaultDate() { AddStep("show autoplay score", () => { @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Ranking { Mods = mods, Beatmap = beatmap, - Date = null, + Date = default, }); }); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index dd053c11f9..e10fe5726d 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -227,8 +227,8 @@ namespace osu.Game.Screens.Ranking.Expanded } }); - if (score.Date != null) - AddInternal(new PlayedOnText(score.Date.Value)); + if (score.Date != default) + AddInternal(new PlayedOnText(score.Date)); if (score.Mods.Any()) { From 9c4fbf45e9d906cc41c0cc2db28b6267b029c644 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:36:12 +0900 Subject: [PATCH 258/489] Add the ability to enter and exit the skin editor via on-screen buttons --- osu.Game/OsuGame.cs | 2 +- .../Overlays/Settings/Sections/SkinSection.cs | 10 ++++- osu.Game/Skinning/Editor/SkinEditor.cs | 7 +++ osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 44 +++++++++++++------ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8119df43ac..6741c1cd13 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -757,7 +757,7 @@ namespace osu.Game loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); loadComponentSingleFile(wikiOverlay = new WikiOverlay(), overlayContent.Add, true); - loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add); + loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add, true); loadComponentSingleFile(new LoginOverlay { diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 316837d27d..e89f3424d9 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -13,6 +13,7 @@ using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Skinning; +using osu.Game.Skinning.Editor; using osuTK; namespace osu.Game.Overlays.Settings.Sections @@ -57,14 +58,19 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerUpdated; private IBindable> managerRemoved; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuConfigManager config, SkinEditorOverlay skinEditor) { FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), + new SettingsButton + { + Text = "Skin layout editor", + Action = () => skinEditor?.Toggle(), + }, new ExportSkinButton(), new SettingsSlider { diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 07a94cac7a..8de64eecb8 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -56,6 +56,13 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new TriangleButton + { + Margin = new MarginPadding(10), + Text = "Close", + Width = 100, + Action = Hide, + }, headerText = new OsuTextFlowContainer { TextAnchor = Anchor.TopCentre, diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 88020896bb..34fac9c9cf 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -38,28 +38,44 @@ namespace osu.Game.Skinning.Editor { case GlobalAction.Back: if (skinEditor?.State.Value == Visibility.Visible) - { - skinEditor.ToggleVisibility(); - return true; - } - - break; + Hide(); + return true; case GlobalAction.ToggleSkinEditor: - if (skinEditor == null) - { - LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); - skinEditor.State.BindValueChanged(editorVisibilityChanged); - } - else - skinEditor.ToggleVisibility(); - + Toggle(); return true; } return false; } + public void Toggle() + { + if (skinEditor == null) + Show(); + else + skinEditor.ToggleVisibility(); + } + + public override void Hide() + { + base.Hide(); + skinEditor.Hide(); + } + + public override void Show() + { + base.Show(); + + if (skinEditor == null) + { + LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); + skinEditor.State.BindValueChanged(editorVisibilityChanged); + } + else + skinEditor.Show(); + } + private void editorVisibilityChanged(ValueChangedEvent visibility) { if (visibility.NewValue == Visibility.Visible) From 59457743e57bda7b2e17c5b1afd88dcba581e2da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:41:52 +0900 Subject: [PATCH 259/489] Move further to the right to avoid overlap with toolbox listing --- osu.Game/Skinning/Editor/SkinEditor.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 8de64eecb8..6c2310ce50 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -56,13 +56,6 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new TriangleButton - { - Margin = new MarginPadding(10), - Text = "Close", - Width = 100, - Action = Hide, - }, headerText = new OsuTextFlowContainer { TextAnchor = Anchor.TopCentre, @@ -95,6 +88,13 @@ namespace osu.Game.Skinning.Editor Children = new Drawable[] { new SkinBlueprintContainer(targetScreen), + new TriangleButton + { + Margin = new MarginPadding(10), + Text = "Close", + Width = 100, + Action = Hide, + }, new FillFlowContainer { Direction = FillDirection.Horizontal, From 16a2e63bd43e8ae2060151599ca23d136418ac38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:44:02 +0900 Subject: [PATCH 260/489] Use existing localisation --- osu.Game/Skinning/Editor/SkinEditor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 6c2310ce50..8052f82c93 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Skinning.Editor @@ -91,7 +92,7 @@ namespace osu.Game.Skinning.Editor new TriangleButton { Margin = new MarginPadding(10), - Text = "Close", + Text = CommonStrings.ButtonsClose, Width = 100, Action = Hide, }, From 9d92b795fac46560120cb35cfb9222459c8e8b1d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 14:15:43 +0300 Subject: [PATCH 261/489] Revert making `ComboOffset`s legacy and define `BeatmapSkinComboIndex` instead --- .../Beatmaps/CatchBeatmapConverter.cs | 3 ++ .../Objects/CatchHitObject.cs | 4 ++ .../TestSceneLegacyBeatmapSkin.cs | 4 +- .../Beatmaps/OsuBeatmapConverter.cs | 6 +-- .../Beatmaps/OsuBeatmapProcessor.cs | 18 --------- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 16 +++++--- .../Formats/LegacyBeatmapDecoderTest.cs | 39 +++++++++++++++---- .../TestSceneHitObjectAccentColour.cs | 3 ++ osu.Game/Beatmaps/BeatmapProcessor.cs | 2 + .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 5 +-- .../Objects/Legacy/Catch/ConvertHit.cs | 2 + .../Legacy/Catch/ConvertHitObjectParser.cs | 14 +++++++ .../Objects/Legacy/Catch/ConvertSlider.cs | 2 + .../Objects/Legacy/Catch/ConvertSpinner.cs | 2 + .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 6 +-- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 +- .../Objects/Legacy/Osu/ConvertSlider.cs | 6 +-- .../Objects/Legacy/Osu/ConvertSpinner.cs | 2 + osu.Game/Rulesets/Objects/Types/IHasCombo.cs | 5 +++ .../Objects/Types/IHasComboInformation.cs | 6 +++ .../Types/IHasLegacyBeatmapComboOffset.cs | 24 ------------ osu.Game/Skinning/LegacyBeatmapSkin.cs | 7 +--- 22 files changed, 102 insertions(+), 78 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index fca2291f41..7774a7da09 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps RepeatCount = curveData.RepeatCount, X = xPositionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, + ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0, LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); @@ -50,6 +51,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, Duration = endTime.Duration, NewCombo = comboData?.NewCombo ?? false, + ComboOffset = comboData?.ComboOffset ?? 0, }.Yield(); default: @@ -58,6 +60,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps StartTime = obj.StartTime, Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, + ComboOffset = comboData?.ComboOffset ?? 0, X = xPositionData?.X ?? 0, LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index f0515ee314..bad558fcad 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -77,6 +77,8 @@ namespace osu.Game.Rulesets.Catch.Objects public virtual bool NewCombo { get; set; } + public int ComboOffset { get; set; } + public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public int IndexInCurrentCombo @@ -93,6 +95,8 @@ namespace osu.Game.Rulesets.Catch.Objects set => ComboIndexBindable.Value = value; } + public int BeatmapSkinComboIndex { get; set; } + public Bindable LastInComboBindable { get; } = new Bindable(); /// diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index a009c560cb..f514753890 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests ConfigureTest(useBeatmapSkin, true, userHasCustomColours); assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", TestBeatmapSkin.Colours, - (i, obj) => i + 1 + obj.LegacyBeatmapComboOffset); + (i, obj) => i + 1 + obj.ComboOffset); } [TestCase(true)] @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = i, Position = new Vector2(256, 192), NewCombo = true, - LegacyBeatmapComboOffset = i, + ComboOffset = i, }); } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index d812f86938..a2fc4848af 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps RepeatCount = curveData.RepeatCount, Position = positionData?.Position ?? Vector2.Zero, NewCombo = comboData?.NewCombo ?? false, - LegacyBeatmapComboOffset = (original as IHasLegacyBeatmapComboOffset)?.LegacyBeatmapComboOffset ?? 0, + ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset, // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. // this results in more (or less) ticks being generated in ()) - { - if (obj.NewCombo) - obj.LegacyBeatmapComboIndex = (lastObj?.LegacyBeatmapComboIndex ?? 0) + obj.LegacyBeatmapComboOffset + 1; - else if (lastObj != null) - obj.LegacyBeatmapComboIndex = lastObj.LegacyBeatmapComboIndex; - - lastObj = obj; - } - } - public override void PostProcess() { base.PostProcess(); diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 6de061978c..db8a02aa0f 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition, IHasLegacyBeatmapComboOffset + public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition { /// /// The radius of hit objects (ie. the radius of a ). @@ -73,6 +73,14 @@ namespace osu.Game.Rulesets.Osu.Objects public virtual bool NewCombo { get; set; } + public readonly Bindable ComboOffsetBindable = new Bindable(); + + public int ComboOffset + { + get => ComboOffsetBindable.Value; + set => ComboOffsetBindable.Value = value; + } + public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public virtual int IndexInCurrentCombo @@ -89,6 +97,8 @@ namespace osu.Game.Rulesets.Osu.Objects set => ComboIndexBindable.Value = value; } + public int BeatmapSkinComboIndex { get; set; } + public Bindable LastInComboBindable { get; } = new Bindable(); public bool LastInCombo @@ -97,10 +107,6 @@ namespace osu.Game.Rulesets.Osu.Objects set => LastInComboBindable.Value = value; } - public int LegacyBeatmapComboOffset { get; set; } - - public int LegacyBeatmapComboIndex { get; set; } - protected OsuHitObject() { StackHeightBindable.BindValueChanged(height => diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index be88bb9b43..42f89a758e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -14,6 +14,8 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; using osu.Game.IO; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; @@ -308,7 +310,7 @@ namespace osu.Game.Tests.Beatmaps.Formats } [Test] - public void TestDecodeLegacyBeatmapComboOffsets() + public void TestDecodeBeatmapComboOffsetsOsu() { var decoder = new LegacyBeatmapDecoder(); @@ -321,12 +323,35 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuBeatmapProcessor(converted).PreProcess(); new OsuBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(0)).LegacyBeatmapComboIndex); - Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(2)).LegacyBeatmapComboIndex); - Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(4)).LegacyBeatmapComboIndex); - Assert.AreEqual(6, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(6)).LegacyBeatmapComboIndex); - Assert.AreEqual(11, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(8)).LegacyBeatmapComboIndex); - Assert.AreEqual(14, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(11)).LegacyBeatmapComboIndex); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); + } + } + + [Test] + public void TestDecodeBeatmapComboOffsetsCatch() + { + var decoder = new LegacyBeatmapDecoder(); + + using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var beatmap = decoder.Decode(stream); + + var converted = new CatchBeatmapConverter(beatmap, new CatchRuleset()).Convert(); + new CatchBeatmapProcessor(converted).PreProcess(); + new CatchBeatmapProcessor(converted).PostProcess(); + + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); } } diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 3dd13da6c1..f6f482e254 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -82,6 +82,7 @@ namespace osu.Game.Tests.Gameplay private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation { public bool NewCombo { get; set; } + public int ComboOffset => 0; public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); @@ -99,6 +100,8 @@ namespace osu.Game.Tests.Gameplay set => ComboIndexBindable.Value = value; } + public int BeatmapSkinComboIndex { get; set; } + public Bindable LastInComboBindable { get; } = new Bindable(); public bool LastInCombo diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index f75f04b26f..9121222bd7 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -38,6 +38,7 @@ namespace osu.Game.Beatmaps { obj.IndexInCurrentCombo = 0; obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + 1; + obj.BeatmapSkinComboIndex = (lastObj?.BeatmapSkinComboIndex ?? 0) + obj.ComboOffset + 1; if (lastObj != null) lastObj.LastInCombo = true; @@ -46,6 +47,7 @@ namespace osu.Game.Beatmaps { obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1; obj.ComboIndex = lastObj.ComboIndex; + obj.BeatmapSkinComboIndex = lastObj.BeatmapSkinComboIndex; } lastObj = obj; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fed8af5b95..f14f6ec10c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -289,11 +289,10 @@ namespace osu.Game.Beatmaps.Formats if (hitObject is IHasCombo combo) { + type = (LegacyHitObjectType)(combo.ComboOffset << 4); + if (combo.NewCombo) type |= LegacyHitObjectType.NewCombo; - - if (hitObject is IHasLegacyBeatmapComboOffset comboOffset) - type |= (LegacyHitObjectType)(comboOffset.LegacyBeatmapComboOffset << 4); } switch (hitObject) diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 0b0a18ba12..12b4812824 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -18,5 +18,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public Vector2 Position { get; set; } public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index d304ea8d39..c29179f749 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -18,16 +18,21 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch } private bool forceNewCombo; + private int extraComboOffset; protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset) { newCombo |= forceNewCombo; + comboOffset += extraComboOffset; + forceNewCombo = false; + extraComboOffset = 0; return new ConvertHit { Position = position, NewCombo = newCombo, + ComboOffset = comboOffset }; } @@ -35,12 +40,16 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch List> nodeSamples) { newCombo |= forceNewCombo; + comboOffset += extraComboOffset; + forceNewCombo = false; + extraComboOffset = 0; return new ConvertSlider { Position = position, NewCombo = FirstObject || newCombo, + ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount @@ -49,6 +58,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { + // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo + // Their combo offset is still added to that next hitobject's combo index + forceNewCombo |= FormatVersion <= 8 || newCombo; + extraComboOffset += comboOffset; + return new ConvertSpinner { Duration = duration diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index 6edf15478f..fb1afed3b4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -18,5 +18,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public Vector2 Position { get; set; } public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 3355bb3ee5..014494ec54 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -17,5 +17,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X => 256; // Required for CatchBeatmapConverter public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 03b103b8fe..069366bad3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset + internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo { public Vector2 Position { get; set; } @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int LegacyBeatmapComboOffset { get; set; } - - int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 451b714266..75ecab0b8f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - LegacyBeatmapComboOffset = comboOffset + ComboOffset = comboOffset }; } @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - LegacyBeatmapComboOffset = comboOffset, + ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index c76c5a3f67..e947690668 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo { public Vector2 Position { get; set; } @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int LegacyBeatmapComboOffset { get; set; } - - int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index 328a380a96..e9e5ca8c94 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -22,5 +22,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public float Y => Position.Y; public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs index 7288684d27..d1a4683a1d 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs @@ -12,5 +12,10 @@ namespace osu.Game.Rulesets.Objects.Types /// Whether the HitObject starts a new combo. /// bool NewCombo { get; } + + /// + /// When starting a new combo, the offset of the new combo relative to the current one. + /// + int ComboOffset { get; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 03e6f76cca..46d7bef498 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -26,6 +26,12 @@ namespace osu.Game.Rulesets.Objects.Types /// int ComboIndex { get; set; } + /// + /// A with the of this and all previous hitobjects applied to it. + /// This is used primarily for beatmap skins during combo colour retrieval, rather than the regular . + /// + int BeatmapSkinComboIndex { get; set; } + /// /// Whether the HitObject starts a new combo. /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs b/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs deleted file mode 100644 index 1a39192438..0000000000 --- a/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A type of that has a combo index with arbitrary offsets applied to use when retrieving legacy beatmap combo colours. - /// This is done in stable for hitobjects to skip combo colours from the beatmap skin (known as "colour hax"). - /// See https://osu.ppy.sh/wiki/en/osu%21_File_Formats/Osu_%28file_format%29#type for more information. - /// - public interface IHasLegacyBeatmapComboOffset - { - /// - /// The legacy offset of the new combo relative to the current one, when starting a new combo. - /// - int LegacyBeatmapComboOffset { get; } - - /// - /// The combo index with the applied, - /// to use for legacy beatmap skins to decide on the combo colour. - /// - int LegacyBeatmapComboIndex { get; set; } - } -} diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 36c9b635c2..5b46a21381 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -63,12 +63,7 @@ namespace osu.Game.Skinning } protected override IBindable GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) - { - if (combo is IHasLegacyBeatmapComboOffset legacyBeatmapCombo) - return base.GetComboColour(source, legacyBeatmapCombo.LegacyBeatmapComboIndex, combo); - - return base.GetComboColour(source, comboIndex, combo); - } + => base.GetComboColour(source, combo.BeatmapSkinComboIndex, combo); public override ISample GetSample(ISampleInfo sampleInfo) { From 300b3f22b1e943aad5d62da0cc3a7db34b116e53 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 14:35:21 +0300 Subject: [PATCH 262/489] Remove no longer correct usage of "legacy" in offsets --- osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index f514753890..f2f68e805c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -84,22 +84,22 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public void TestLegacyOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) + public void TestComboOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); ConfigureTest(useBeatmapSkin, true, userHasCustomColours); - assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", + assertCorrectObjectComboColours("is beatmap skin colours with combo offsets applied", TestBeatmapSkin.Colours, (i, obj) => i + 1 + obj.ComboOffset); } [TestCase(true)] [TestCase(false)] - public void TestLegacyOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) + public void TestComboOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); ConfigureTest(useBeatmapSkin, false, true); - assertCorrectObjectComboColours("is user skin colours without legacy offsets applied", + assertCorrectObjectComboColours("is user skin colours without combo offsets applied", TestSkin.Colours, (i, _) => i + 1); } From 1e634d9db091ea1ba71ddfa90a4ffd06c5e4aa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 20 Jul 2021 21:48:38 +0200 Subject: [PATCH 263/489] Adjust user beatmap sections on profile overlay to match web --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 4 ++-- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 8 ++++---- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 4 ++-- osu.Game/Users/User.cs | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index fb1385793f..e2c0ed4301 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -26,9 +26,9 @@ namespace osu.Game.Online.API.Requests public enum BeatmapSetType { Favourite, - RankedAndApproved, + Ranked, Loved, - Unranked, + Pending, Graveyard } } diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index ec64371a5d..8657e356c9 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps case BeatmapSetType.Loved: return user.LovedBeatmapsetCount; - case BeatmapSetType.RankedAndApproved: - return user.RankedAndApprovedBeatmapsetCount; + case BeatmapSetType.Ranked: + return user.RankedBeatmapsetCount; - case BeatmapSetType.Unranked: - return user.UnrankedBeatmapsetCount; + case BeatmapSetType.Pending: + return user.PendingBeatmapsetCount; default: return 0; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 843ab531be..af6ab4aad1 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Profile.Sections Children = new[] { new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, UsersStrings.ShowExtraBeatmapsFavouriteTitle), - new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, UsersStrings.ShowExtraBeatmapsRankedTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Ranked, User, UsersStrings.ShowExtraBeatmapsRankedTitle), new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, UsersStrings.ShowExtraBeatmapsLovedTitle), - new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, UsersStrings.ShowExtraBeatmapsPendingTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Pending, User, UsersStrings.ShowExtraBeatmapsPendingTitle), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, UsersStrings.ShowExtraBeatmapsGraveyardTitle) }; } diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 2e04693e82..20c23153f0 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -138,11 +138,11 @@ namespace osu.Game.Users [JsonProperty(@"loved_beatmapset_count")] public int LovedBeatmapsetCount; - [JsonProperty(@"ranked_and_approved_beatmapset_count")] - public int RankedAndApprovedBeatmapsetCount; + [JsonProperty(@"ranked_beatmapset_count")] + public int RankedBeatmapsetCount; - [JsonProperty(@"unranked_beatmapset_count")] - public int UnrankedBeatmapsetCount; + [JsonProperty(@"pending_beatmapset_count")] + public int PendingBeatmapsetCount; [JsonProperty(@"scores_best_count")] public int ScoresBestCount; From bfec87b0829294c59500c1e4cc204686c2bc7371 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 20 Jul 2021 22:25:17 +0200 Subject: [PATCH 264/489] Let TimelineBlueprintContainer only accept positional input within timeline quad --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 8e0b39513a..69db191cce 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -35,8 +35,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable placement; private SelectionBlueprint placementBlueprint; - // We want children to be able to be clicked and dragged, regardless of this drawable's size - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + // We want children within the timeline to be interactable + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); public TimelineBlueprintContainer(HitObjectComposer composer) : base(composer) From a8cf6a6854231bc2f7fbeffee45d97460c2dd812 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 20 Jul 2021 23:00:58 +0200 Subject: [PATCH 265/489] Fix slight Y position offset in HandleDrag --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 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 69db191cce..a5210132a3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -309,7 +309,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool HandleDrag(MouseButtonEvent e) { // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. - if (DrawRectangle.Top > e.MouseDownPosition.Y || DrawRectangle.Bottom < e.MouseDownPosition.Y) + float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y; + if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY) return false; selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; From db9cf443c7b5c2d76b06fe40f4993ad2bccc20a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 21 Jul 2021 00:04:51 +0200 Subject: [PATCH 266/489] Allow confirming room password by pressing Enter --- .../TestSceneMultiplayerLoungeSubScreen.cs | 16 ++++++++++++++++ .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 1 + 2 files changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index de46d9e25a..4ea635fd3e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -80,6 +80,22 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room join password correct", () => lastJoinedPassword == "password"); } + [Test] + public void TestJoinRoomWithPasswordViaKeyboardOnly() + { + DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); + AddStep("press enter", () => InputManager.Key(Key.Enter)); + + AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First()); + AddAssert("room join password correct", () => lastJoinedPassword == "password"); + } + private void onRoomJoined(Room room, string password) { lastJoinedRoom = room; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 236408851f..940ae873ec 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -390,6 +390,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components base.LoadComplete(); Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); + passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text); } } } From 37393a84320ca08d3ace82a07978a27cf3b1746a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 21 Jul 2021 03:12:44 +0300 Subject: [PATCH 267/489] Allow defining custom storage name for debug builds of osu!lazer --- osu.Desktop/Program.cs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 5fb09c0cef..b17fe87538 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -23,7 +23,16 @@ namespace osu.Desktop // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; - using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) + string gameName = @"osu"; + + if (DebugUtils.IsDebugBuild) + { + var customNameArg = args.SingleOrDefault(s => s.StartsWith(@"--name=", StringComparison.Ordinal)); + if (customNameArg != null) + gameName = customNameArg.Replace(@"--name=", string.Empty); + } + + using (DesktopGameHost host = Host.GetSuitableHost(gameName, true)) { host.ExceptionThrown += handleException; @@ -48,16 +57,10 @@ namespace osu.Desktop return 0; } - switch (args.FirstOrDefault() ?? string.Empty) - { - default: - host.Run(new OsuGameDesktop(args)); - break; - - case "--tournament": - host.Run(new TournamentGame()); - break; - } + if (args.Contains("--tournament")) + host.Run(new TournamentGame()); + else + host.Run(new OsuGameDesktop(args)); return 0; } From ca3dfb2498c589fc0019e50c0e161e8142ec2739 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 12:53:48 +0900 Subject: [PATCH 268/489] Fix comment --- .../Edit/Blueprints/Components/SelectionEditablePath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 3acb7f43a6..b643ca9680 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components else if (!VertexStates[index].IsSelected) selectOnly(index); - // Don't inhabit right click, to show the context menu + // Don't inhibit right click, to show the context menu return e.Button != MouseButton.Right; } From 97fba5df583e1b471fba7d8840cc180ae94b4a20 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 12:59:42 +0900 Subject: [PATCH 269/489] Use existing method for the same code --- .../Edit/Blueprints/Components/SelectionEditablePath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index b643ca9680..6c08b59e09 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override void OnDrag(DragEvent e) { - Vector2 mousePosition = ToLocalSpace(e.ScreenSpaceMousePosition) - new Vector2(0, DrawHeight); + Vector2 mousePosition = ToRelativePosition(e.ScreenSpaceMousePosition); double distanceDelta = PositionToDistance(mousePosition.Y) - PositionToDistance(dragStartPosition.Y); float xDelta = mousePosition.X - dragStartPosition.X; MoveSelectedVertices(distanceDelta, xDelta); From cc0110aa5267262a1dfea8185af8d423227e742b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 13:17:13 +0900 Subject: [PATCH 270/489] Add doc comment to `VertexState` --- .../Edit/Blueprints/Components/VertexState.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs index 55abbf7475..3f240c7944 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs @@ -7,12 +7,25 @@ using osu.Game.Rulesets.Catch.Objects; namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { + /// + /// Holds the state of a vertex in the path of a . + /// public class VertexState { + /// + /// Whether the vertex is selected. + /// public bool IsSelected { get; set; } + /// + /// Whether the vertex can be moved or deleted. + /// public bool IsFixed { get; set; } + /// + /// The position of the vertex before a vertex moving operation starts. + /// This is used to implement "memory-less" moving operations (only the final position matters) to improve UX. + /// public JuiceStreamPathVertex VertexBeforeChange { get; set; } } } From 2b0d5300750ff2f6a78f541c51be1a2ade92c131 Mon Sep 17 00:00:00 2001 From: Anton Kovalyov Date: Tue, 20 Jul 2021 21:18:24 -0700 Subject: [PATCH 271/489] Eliminate Overlay.KeyBinding namespace and move everything to Settings.Section.Input --- osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs | 2 +- .../Sections/Input}/GlobalKeyBindingsSection.cs | 2 +- .../Overlays/{ => Settings/Sections/Input}/KeyBindingPanel.cs | 4 ++-- .../{KeyBinding => Settings/Sections/Input}/KeyBindingRow.cs | 2 +- .../Sections/Input}/KeyBindingsSubsection.cs | 2 +- .../Sections/Input}/RulesetBindingsSection.cs | 2 +- .../Sections/Input}/VariantBindingsSubsection.cs | 2 +- osu.Game/Overlays/SettingsOverlay.cs | 1 + 8 files changed, 9 insertions(+), 8 deletions(-) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/GlobalKeyBindingsSection.cs (98%) rename osu.Game/Overlays/{ => Settings/Sections/Input}/KeyBindingPanel.cs (89%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/KeyBindingRow.cs (99%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/KeyBindingsSubsection.cs (97%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/RulesetBindingsSection.cs (94%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/VariantBindingsSubsection.cs (93%) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index acf9deb3cb..54293485cb 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -8,7 +8,7 @@ using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; -using osu.Game.Overlays.KeyBinding; +using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Input; namespace osu.Game.Tests.Visual.Settings diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs similarity index 98% rename from osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs rename to osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs index 6ea4209cce..4918c3b97c 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs @@ -7,7 +7,7 @@ using osu.Framework.Localisation; using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class GlobalKeyBindingsSection : SettingsSection { diff --git a/osu.Game/Overlays/KeyBindingPanel.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs similarity index 89% rename from osu.Game/Overlays/KeyBindingPanel.cs rename to osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs index 928bd080fa..86c31ee1b2 100644 --- a/osu.Game/Overlays/KeyBindingPanel.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs @@ -4,11 +4,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Input.Bindings; -using osu.Game.Overlays.KeyBinding; using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Rulesets; -namespace osu.Game.Overlays +namespace osu.Game.Overlays.Settings.Sections.Input { public class KeyBindingPanel : SettingsSubPanel { diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs similarity index 99% rename from osu.Game/Overlays/KeyBinding/KeyBindingRow.cs rename to osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index ef620df171..4f7deebb5b 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -24,7 +24,7 @@ using osuTK; using osuTK.Graphics; using osuTK.Input; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class KeyBindingRow : Container, IFilterable { diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs similarity index 97% rename from osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs rename to osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 1fdc1b6574..beee1650a5 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -13,7 +13,7 @@ using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public abstract class KeyBindingsSubsection : SettingsSubsection { diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs similarity index 94% rename from osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs rename to osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs index 332fb6c8fc..d2fb811e73 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class RulesetBindingsSection : SettingsSection { diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/VariantBindingsSubsection.cs similarity index 93% rename from osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs rename to osu.Game/Overlays/Settings/Sections/Input/VariantBindingsSubsection.cs index 7618a42282..a0f069b3bb 100644 --- a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/VariantBindingsSubsection.cs @@ -4,7 +4,7 @@ using osu.Framework.Localisation; using osu.Game.Rulesets; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class VariantBindingsSubsection : KeyBindingsSubsection { diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 8c21880cc6..54b780615d 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; +using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Graphics; using System.Collections.Generic; using System.Linq; From cd447f03059e2e66b7c306dd455ab5028d1ec90e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 13:27:07 +0900 Subject: [PATCH 272/489] Add some doc comment to `JuiceStreamSelectionBlueprint` --- .../Edit/Blueprints/JuiceStreamSelectionBlueprint.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index defb5eae8b..890d059d19 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -37,8 +37,16 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private readonly SelectionEditablePath editablePath; + /// + /// The of the corresponding the current of the hit object. + /// When the path is edited, the change is detected and the of the hit object is updated. + /// private int lastEditablePathId = -1; + /// + /// The of the current of the hit object. + /// When the of the hit object is changed by external means, the change is detected and the is re-initialized. + /// private int lastSliderPathVersion = -1; private Vector2 rightMouseDownPosition; From ebd555129f81e46ec6f73eeeaa4f49e619723fc6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 21 Jul 2021 07:53:24 +0300 Subject: [PATCH 273/489] Change to `int`-only debug client ID --- osu.Desktop/Program.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index b17fe87538..0bcbc696bc 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -24,12 +24,23 @@ namespace osu.Desktop var cwd = Environment.CurrentDirectory; string gameName = @"osu"; + bool tournamentClient = false; - if (DebugUtils.IsDebugBuild) + foreach (var arg in args.Select(s => s.Split('='))) { - var customNameArg = args.SingleOrDefault(s => s.StartsWith(@"--name=", StringComparison.Ordinal)); - if (customNameArg != null) - gameName = customNameArg.Replace(@"--name=", string.Empty); + switch (arg[0]) + { + case "--tournament": + tournamentClient = true; + break; + + case "--debug-client-id": + if (!DebugUtils.IsDebugBuild) + break; + + gameName = $"{gameName}-{int.Parse(arg[1])}"; + break; + } } using (DesktopGameHost host = Host.GetSuitableHost(gameName, true)) @@ -57,7 +68,7 @@ namespace osu.Desktop return 0; } - if (args.Contains("--tournament")) + if (tournamentClient) host.Run(new TournamentGame()); else host.Run(new OsuGameDesktop(args)); From bfad044b0018aefcf51d559a4ac182d653cb7960 Mon Sep 17 00:00:00 2001 From: Anton Kovalyov Date: Tue, 20 Jul 2021 21:57:55 -0700 Subject: [PATCH 274/489] Remove unused imports. --- .../Settings/Sections/Input/GlobalKeyBindingsSection.cs | 1 - osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs | 2 -- .../Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs | 1 - .../Overlays/Settings/Sections/Input/RulesetBindingsSection.cs | 1 - 4 files changed, 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs index 4918c3b97c..9898a50320 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Settings; namespace osu.Game.Overlays.Settings.Sections.Input { diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs index 86c31ee1b2..7cdc739b7c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs @@ -4,8 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Settings; -using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Rulesets; namespace osu.Game.Overlays.Settings.Sections.Input diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index beee1650a5..d65684fd37 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; diff --git a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs index d2fb811e73..81a4d7eccd 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets; namespace osu.Game.Overlays.Settings.Sections.Input From 4148d3fdac4437e4f4246c66d908e18979c9e4cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:02:15 +0900 Subject: [PATCH 275/489] Add a bit more safety to argument parsing logic --- osu.Desktop/Program.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 0bcbc696bc..0b81e2d127 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -17,18 +17,25 @@ namespace osu.Desktop { public static class Program { + private const string base_game_name = @"osu"; + [STAThread] public static int Main(string[] args) { // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; - string gameName = @"osu"; + string gameName = base_game_name; bool tournamentClient = false; - foreach (var arg in args.Select(s => s.Split('='))) + foreach (var arg in args) { - switch (arg[0]) + var split = arg.Split('='); + + var key = split[0]; + var val = split[1]; + + switch (key) { case "--tournament": tournamentClient = true; @@ -36,9 +43,12 @@ namespace osu.Desktop case "--debug-client-id": if (!DebugUtils.IsDebugBuild) - break; + throw new InvalidOperationException("Cannot use this argument in a non-debug build."); - gameName = $"{gameName}-{int.Parse(arg[1])}"; + if (!int.TryParse(val, out int clientID)) + throw new ArgumentException("Provided client ID must be an integer."); + + gameName = $"{base_game_name}-{clientID}"; break; } } From 3a968977cb249abc8945b123f24cc7a5f42a8083 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:09:50 +0900 Subject: [PATCH 276/489] Add rider configuration for dual-client testing --- .run/Dual client test.run.xml | 7 +++++++ .run/osu! (Second Client).run.xml | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 .run/Dual client test.run.xml create mode 100644 .run/osu! (Second Client).run.xml diff --git a/.run/Dual client test.run.xml b/.run/Dual client test.run.xml new file mode 100644 index 0000000000..e112aa3d5d --- /dev/null +++ b/.run/Dual client test.run.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.run/osu! (Second Client).run.xml b/.run/osu! (Second Client).run.xml new file mode 100644 index 0000000000..599b4b986b --- /dev/null +++ b/.run/osu! (Second Client).run.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file From 3dddcf3582247e3325f38d032664df9b614a69f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:11:01 +0900 Subject: [PATCH 277/489] Remove unused using statement --- osu.Desktop/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 0b81e2d127..cbee1694ba 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework; From 0118c3638c0ba2285569a6698163cfe112a644d9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Jul 2021 15:59:02 +0900 Subject: [PATCH 278/489] Fix beatmap listing continually paginating for null cursors --- .../Overlays/BeatmapListing/BeatmapListingFilterControl.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 650d105911..3d8c2b1fe6 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -189,6 +189,10 @@ namespace osu.Game.Overlays.BeatmapListing private void performRequest() { + // If the previous request returned a null cursor, the API is indicating we can't paginate further (maybe there are no more beatmaps left). + if (lastResponse != null && lastResponse.Cursor == null) + return; + getSetsRequest = new SearchBeatmapSetsRequest( searchControl.Query.Value, searchControl.Ruleset.Value, From 1bff4373b3779c6d418b7bbe5a3e94f88cdf0510 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:59:25 +0900 Subject: [PATCH 279/489] Allow specifying flipping support of selection box different from scaling --- .../Edit/OsuSelectionHandler.cs | 4 +- .../Editing/TestSceneComposeSelectBox.cs | 2 + .../Edit/Compose/Components/SelectionBox.cs | 58 ++++++++++++++++--- .../Skinning/Editor/SkinSelectionHandler.cs | 2 + 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 57d0cd859d..c2c2226af0 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Osu.Edit Quad quad = selectedMovableObjects.Length > 0 ? getSurroundingQuad(selectedMovableObjects) : new Quad(); SelectionBox.CanRotate = quad.Width > 0 || quad.Height > 0; - SelectionBox.CanScaleX = quad.Width > 0; - SelectionBox.CanScaleY = quad.Height > 0; + SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0; + SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0; SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index d5cfeb1878..87dbb90138 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -35,6 +35,8 @@ namespace osu.Game.Tests.Visual.Editing CanRotate = true, CanScaleX = true, CanScaleY = true, + CanFlipX = true, + CanFlipY = true, OnRotation = handleRotation, OnScale = handleScale diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index dc457b5320..be52a968bb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool canScaleX; /// - /// Whether vertical scale support should be enabled. + /// Whether horizontal scaling support should be enabled. /// public bool CanScaleX { @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool canScaleY; /// - /// Whether horizontal scale support should be enabled. + /// Whether vertical scaling support should be enabled. /// public bool CanScaleY { @@ -95,6 +95,40 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + private bool canFlipX; + + /// + /// Whether horizontal flipping support should be enabled. + /// + public bool CanFlipX + { + get => canFlipX; + set + { + if (canFlipX == value) return; + + canFlipX = value; + recreate(); + } + } + + private bool canFlipY; + + /// + /// Whether vertical flipping support should be enabled. + /// + public bool CanFlipY + { + get => canFlipY; + set + { + if (canFlipY == value) return; + + canFlipY = value; + recreate(); + } + } + private string text; public string Text @@ -142,10 +176,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return CanReverse && runOperationFromHotkey(OnReverse); case Key.H: - return CanScaleX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false); + return CanFlipX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false); case Key.J: - return CanScaleY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false); + return CanFlipY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false); } return base.OnKeyDown(e); @@ -214,6 +248,8 @@ namespace osu.Game.Screens.Edit.Compose.Components if (CanScaleX) addXScaleComponents(); if (CanScaleX && CanScaleY) addFullScaleComponents(); if (CanScaleY) addYScaleComponents(); + if (CanFlipX) addXFlipComponents(); + if (CanFlipY) addYFlipComponents(); if (CanRotate) addRotationComponents(); if (CanReverse) addButton(FontAwesome.Solid.Backward, "Reverse pattern (Ctrl-G)", () => OnReverse?.Invoke()); } @@ -231,8 +267,6 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addYScaleComponents() { - addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically (Ctrl-J)", () => OnFlip?.Invoke(Direction.Vertical)); - addScaleHandle(Anchor.TopCentre); addScaleHandle(Anchor.BottomCentre); } @@ -247,12 +281,20 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addXScaleComponents() { - addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally (Ctrl-H)", () => OnFlip?.Invoke(Direction.Horizontal)); - addScaleHandle(Anchor.CentreLeft); addScaleHandle(Anchor.CentreRight); } + private void addXFlipComponents() + { + addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally (Ctrl-H)", () => OnFlip?.Invoke(Direction.Horizontal)); + } + + private void addYFlipComponents() + { + addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically (Ctrl-J)", () => OnFlip?.Invoke(Direction.Vertical)); + } + private void addButton(IconUsage icon, string tooltip, Action action) { var button = new SelectionBoxButton(icon, tooltip) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 17eb88226d..0790faad34 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -170,6 +170,8 @@ namespace osu.Game.Skinning.Editor SelectionBox.CanRotate = true; SelectionBox.CanScaleX = true; SelectionBox.CanScaleY = true; + SelectionBox.CanFlipX = true; + SelectionBox.CanFlipY = true; SelectionBox.CanReverse = false; } From bcd1a3c23234590ec6f92691080c57ea91f9f0df Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Jul 2021 16:04:07 +0900 Subject: [PATCH 280/489] Use existing bool --- .../Overlays/BeatmapListing/BeatmapListingFilterControl.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 3d8c2b1fe6..03d36ff5df 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -189,10 +189,6 @@ namespace osu.Game.Overlays.BeatmapListing private void performRequest() { - // If the previous request returned a null cursor, the API is indicating we can't paginate further (maybe there are no more beatmaps left). - if (lastResponse != null && lastResponse.Cursor == null) - return; - getSetsRequest = new SearchBeatmapSetsRequest( searchControl.Query.Value, searchControl.Ruleset.Value, @@ -212,7 +208,8 @@ namespace osu.Game.Overlays.BeatmapListing { var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList(); - if (sets.Count == 0) + // If the previous request returned a null cursor, the API is indicating we can't paginate further (maybe there are no more beatmaps left). + if (sets.Count == 0 || response.Cursor == null) noMoreResults = true; if (CurrentPage == 0) From 60f876511d5de96629fe3a2e53b83e02d7e8a34f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:36:34 +0900 Subject: [PATCH 281/489] Add function of computing position range occupied by hit objects --- .../Edit/CatchHitObjectUtils.cs | 39 +++++++++++++++++++ osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 33 ++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/PositionRange.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs index beffdf0362..b059926668 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs @@ -1,7 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -20,5 +23,41 @@ namespace osu.Game.Rulesets.Catch.Edit { return new Vector2(hitObject.OriginalX, hitObjectContainer.PositionAtTime(hitObject.StartTime)); } + + /// + /// Get the range of horizontal position occupied by the hit object. + /// + /// + /// s are excluded and returns . + /// + public static PositionRange GetPositionRange(HitObject hitObject) + { + switch (hitObject) + { + case Fruit fruit: + return new PositionRange(fruit.OriginalX); + + case Droplet droplet: + return droplet is TinyDroplet ? PositionRange.EMPTY : new PositionRange(droplet.OriginalX); + + case JuiceStream _: + return GetPositionRange(hitObject.NestedHitObjects); + + case BananaShower _: + // A banana shower occupies the whole screen width. + return new PositionRange(0, CatchPlayfield.WIDTH); + + default: + return PositionRange.EMPTY; + } + } + + /// + /// Get the range of horizontal position occupied by the hit objects. + /// + /// + /// s are excluded. + /// + public static PositionRange GetPositionRange(IEnumerable hitObjects) => hitObjects.Select(GetPositionRange).Aggregate(PositionRange.EMPTY, PositionRange.Union); } } diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs new file mode 100644 index 0000000000..6ed1ac2c06 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Edit +{ + /// + /// Represents a closed interval of horizontal positions in the playfield. + /// + public readonly struct PositionRange + { + public readonly float Min; + public readonly float Max; + + public PositionRange(float value) + : this(value, value) + { + } + + public PositionRange(float min, float max) + { + Min = min; + Max = max; + } + + public static PositionRange Union(PositionRange a, PositionRange b) => new PositionRange(Math.Min(a.Min, b.Min), Math.Max(a.Max, b.Max)); + + public static readonly PositionRange EMPTY = new PositionRange(float.PositiveInfinity, float.NegativeInfinity); + } +} From 4c8b9c168e4c32320aa3affdd874688663419158 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:37:42 +0900 Subject: [PATCH 282/489] Use added position range computation in hit object move handling --- .../Edit/CatchSelectionHandler.cs | 46 ++----------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 7eebf04ca2..ffb342f772 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -59,20 +59,12 @@ namespace osu.Game.Rulesets.Catch.Edit /// The positional movement with the restriction applied. private float limitMovement(float deltaX, IEnumerable movingObjects) { - float minX = float.PositiveInfinity; - float maxX = float.NegativeInfinity; - - foreach (float x in movingObjects.SelectMany(getOriginalPositions)) - { - minX = Math.Min(minX, x); - maxX = Math.Max(maxX, x); - } - + var range = CatchHitObjectUtils.GetPositionRange(movingObjects); // To make an object with position `x` stay in bounds after `deltaX` movement, `0 <= x + deltaX <= WIDTH` should be satisfied. // Subtracting `x`, we get `-x <= deltaX <= WIDTH - x`. // We only need to apply the inequality to extreme values of `x`. - float lowerBound = -minX; - float upperBound = CatchPlayfield.WIDTH - maxX; + float lowerBound = -range.Min; + float upperBound = CatchPlayfield.WIDTH - range.Max; // The inequality may be unsatisfiable if the objects were already out of bounds. // In that case, don't move objects at all. if (lowerBound > upperBound) @@ -80,37 +72,5 @@ namespace osu.Game.Rulesets.Catch.Edit return Math.Clamp(deltaX, lowerBound, upperBound); } - - /// - /// Enumerate X positions that should be contained in-bounds after move offset is applied. - /// - private IEnumerable getOriginalPositions(HitObject hitObject) - { - switch (hitObject) - { - case Fruit fruit: - yield return fruit.OriginalX; - - break; - - case JuiceStream juiceStream: - foreach (var nested in juiceStream.NestedHitObjects.OfType()) - { - // Even if `OriginalX` is outside the playfield, tiny droplets can be moved inside the playfield after the random offset application. - if (!(nested is TinyDroplet)) - yield return nested.OriginalX; - } - - break; - - case BananaShower _: - // A banana shower occupies the whole screen width. - // If the selection contains a banana shower, the selection cannot be moved horizontally. - yield return 0; - yield return CatchPlayfield.WIDTH; - - break; - } - } } } From d2d3214d47958703be35f6117b15bfd1541d2a1d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:47:16 +0900 Subject: [PATCH 283/489] Implement horizontal flipping of hit objects in catch editor --- .../Edit/CatchSelectionHandler.cs | 43 +++++++++++++++++++ osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 2 + 2 files changed, 45 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index ffb342f772..50290ae292 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; using osuTK; +using Direction = osu.Framework.Graphics.Direction; namespace osu.Game.Rulesets.Catch.Edit { @@ -51,6 +52,26 @@ namespace osu.Game.Rulesets.Catch.Edit return true; } + public override bool HandleFlip(Direction direction) + { + var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); + + bool changed = false; + EditorBeatmap.PerformOnSelection(h => + { + if (h is CatchHitObject hitObject) + changed |= handleFlip(selectionRange, hitObject); + }); + return changed; + } + + protected override void OnSelectionChanged() + { + base.OnSelectionChanged(); + + SelectionBox.CanFlipX = true; + } + /// /// Limit positional movement of the objects by the constraint that moved objects should stay in bounds. /// @@ -72,5 +93,27 @@ namespace osu.Game.Rulesets.Catch.Edit return Math.Clamp(deltaX, lowerBound, upperBound); } + + private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject) + { + switch (hitObject) + { + case BananaShower _: + return false; + + case JuiceStream juiceStream: + juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX); + + foreach (var point in juiceStream.Path.ControlPoints) + point.Position.Value *= new Vector2(-1, 1); + + EditorBeatmap.Update(juiceStream); + return true; + + default: + hitObject.OriginalX = selectionRange.GetFlippedPosition(hitObject.OriginalX); + return true; + } + } } } diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs index 6ed1ac2c06..64e789483a 100644 --- a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.Edit public static PositionRange Union(PositionRange a, PositionRange b) => new PositionRange(Math.Min(a.Min, b.Min), Math.Max(a.Max, b.Max)); + public float GetFlippedPosition(float x) => Max - (x - Min); + public static readonly PositionRange EMPTY = new PositionRange(float.PositiveInfinity, float.NegativeInfinity); } } From 1bac471b49bf9cc34154a3e182c5945ba6284d77 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 20 Jul 2021 14:23:34 +0900 Subject: [PATCH 284/489] Adapt to `PlatformAction` type change --- .../Components/PathControlPointVisualiser.cs | 4 ++-- .../Visual/Online/TestSceneChatOverlay.cs | 10 +++++----- osu.Game/Graphics/UserInterface/SearchTextBox.cs | 15 +++++---------- osu.Game/Overlays/ChatOverlay.cs | 8 ++++---- .../Edit/Compose/Components/BlueprintContainer.cs | 4 ++-- .../Edit/Compose/Components/SelectionHandler.cs | 4 ++-- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 14 +++++++------- 8 files changed, 28 insertions(+), 33 deletions(-) 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 c36768baba..5bbdf9688f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -129,9 +129,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public bool OnPressed(PlatformAction action) { - switch (action.ActionMethod) + switch (action) { - case PlatformActionMethod.Delete: + case PlatformAction.Delete: return DeleteSelected(); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index a1549dfbce..5e234bdacf 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -330,15 +330,15 @@ namespace osu.Game.Tests.Visual.Online InputManager.ReleaseKey(Key.AltLeft); } - private void pressCloseDocumentKeys() => pressKeysFor(PlatformActionType.DocumentClose); + private void pressCloseDocumentKeys() => pressKeysFor(PlatformAction.DocumentClose); - private void pressNewTabKeys() => pressKeysFor(PlatformActionType.TabNew); + private void pressNewTabKeys() => pressKeysFor(PlatformAction.TabNew); - private void pressRestoreTabKeys() => pressKeysFor(PlatformActionType.TabRestore); + private void pressRestoreTabKeys() => pressKeysFor(PlatformAction.TabRestore); - private void pressKeysFor(PlatformActionType type) + private void pressKeysFor(PlatformAction type) { - var binding = host.PlatformKeyBindings.First(b => ((PlatformAction)b.Action).ActionType == type); + var binding = host.PlatformKeyBindings.First(b => (PlatformAction)b.Action == type); foreach (var k in binding.KeyCombination.Keys) InputManager.PressKey((Key)k); diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index fe92054d25..4a91741ce6 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -32,20 +32,15 @@ namespace osu.Game.Graphics.UserInterface public override bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.LineEnd: - case PlatformActionType.LineStart: - return false; - + case PlatformAction.MoveBackwardLine: + case PlatformAction.MoveForwardLine: // Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox // as we do not allow arrow key navigation in the first place (ie. the caret should always be at the end of text) // Avoid handling it here to allow other components to potentially consume the shortcut. - case PlatformActionType.CharNext: - if (action.ActionMethod == PlatformActionMethod.Delete) - return false; - - break; + case PlatformAction.DeleteForwardChar: + return false; } return base.OnPressed(action); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 0aa6108815..0445c63eb4 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -374,17 +374,17 @@ namespace osu.Game.Overlays public bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.TabNew: + case PlatformAction.TabNew: ChannelTabControl.SelectChannelSelectorTab(); return true; - case PlatformActionType.TabRestore: + case PlatformAction.TabRestore: channelManager.JoinLastClosedChannel(); return true; - case PlatformActionType.DocumentClose: + case PlatformAction.DocumentClose: channelManager.LeaveChannel(channelManager.CurrentChannel.Value); return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 185f029d14..0432cdffc0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -230,9 +230,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.SelectAll: + case PlatformAction.SelectAll: SelectAll(); return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 8939be925a..1d1d95890f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -139,9 +139,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public bool OnPressed(PlatformAction action) { - switch (action.ActionMethod) + switch (action) { - case PlatformActionMethod.Delete: + case PlatformAction.Delete: DeleteSelected(); return true; } diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index b56f9bee14..4a1f1196a9 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose public bool OnPressed(PlatformAction action) { - if (action.ActionType == PlatformActionType.Copy) + if (action == PlatformAction.Copy) host.GetClipboard().SetText(formatSelectionAsString()); return false; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 71dd47b058..b6dc97a7f6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -330,29 +330,29 @@ namespace osu.Game.Screens.Edit public bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.Cut: + case PlatformAction.Cut: Cut(); return true; - case PlatformActionType.Copy: + case PlatformAction.Copy: Copy(); return true; - case PlatformActionType.Paste: + case PlatformAction.Paste: Paste(); return true; - case PlatformActionType.Undo: + case PlatformAction.Undo: Undo(); return true; - case PlatformActionType.Redo: + case PlatformAction.Redo: Redo(); return true; - case PlatformActionType.Save: + case PlatformAction.Save: Save(); return true; } From da3499f10edc72ab2170d9fc688d028aadc0b1a8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:27:44 +0900 Subject: [PATCH 285/489] Simplify the way of getting catcher --- osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index db09b2bc6b..163fee49fb 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests // this needs to be done within the frame stable context due to how quickly hyperdash state changes occur. Player.DrawableRuleset.FrameStableComponents.OnUpdate += d => { - var catcher = Player.ChildrenOfType().FirstOrDefault()?.MovableCatcher; + var catcher = Player.ChildrenOfType().FirstOrDefault(); if (catcher == null) return; From 94678064ff3a8b8fa56907c06ce50e39489bc8f8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:28:31 +0900 Subject: [PATCH 286/489] Rename `CatcherArea.MovableCatcher` to `Catcher` --- .../TestSceneCatcherArea.cs | 6 ++--- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 26 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 5a2e5b60f5..6a518cf0ef 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Catch.Tests { area.OnNewResult(drawable, new CatchJudgementResult(fruit, new CatchJudgement()) { - Type = area.MovableCatcher.CanCatch(fruit) ? HitResult.Great : HitResult.Miss + Type = area.Catcher.CanCatch(fruit) ? HitResult.Great : HitResult.Miss }); drawable.Expire(); @@ -125,13 +125,13 @@ namespace osu.Game.Rulesets.Catch.Tests Add(droppedObjectContainer); - MovableCatcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) + Catcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; } - public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1); + public void ToggleHyperDash(bool status) => Catcher.SetHyperDashState(status ? 2 : 1); } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 6f84793744..b43815a8bd 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Catch.UI { Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, - MovableCatcher = Catcher, + Catcher = Catcher, }, trailContainer, HitObjectContainer, diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index a9d0275678..f2c5f96b26 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 106.75f; - public Catcher MovableCatcher + public Catcher Catcher { get => catcher; set @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.UI public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result) { - MovableCatcher.OnNewResult(hitObject, result); + Catcher.OnNewResult(hitObject, result); if (!result.Type.IsScorable()) return; @@ -66,9 +66,9 @@ namespace osu.Game.Rulesets.Catch.UI if (hitObject.HitObject.LastInCombo) { if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result)) - MovableCatcher.Explode(); + Catcher.Explode(); else - MovableCatcher.Drop(); + Catcher.Drop(); } comboDisplay.OnNewResult(hitObject, result); @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Catch.UI public void OnRevertResult(DrawableCatchHitObject hitObject, JudgementResult result) { comboDisplay.OnRevertResult(hitObject, result); - MovableCatcher.OnRevertResult(hitObject, result); + Catcher.OnRevertResult(hitObject, result); } protected override void Update() @@ -88,27 +88,27 @@ namespace osu.Game.Rulesets.Catch.UI SetCatcherPosition( replayState?.CatcherX ?? - (float)(MovableCatcher.X + MovableCatcher.Speed * currentDirection * Clock.ElapsedFrameTime)); + (float)(Catcher.X + Catcher.Speed * currentDirection * Clock.ElapsedFrameTime)); } protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - comboDisplay.X = MovableCatcher.X; + comboDisplay.X = Catcher.X; } public void SetCatcherPosition(float X) { - float lastPosition = MovableCatcher.X; + float lastPosition = Catcher.X; float newPosition = Math.Clamp(X, 0, CatchPlayfield.WIDTH); - MovableCatcher.X = newPosition; + Catcher.X = newPosition; if (lastPosition < newPosition) - MovableCatcher.VisualDirection = Direction.Right; + Catcher.VisualDirection = Direction.Right; else if (lastPosition > newPosition) - MovableCatcher.VisualDirection = Direction.Left; + Catcher.VisualDirection = Direction.Left; } public bool OnPressed(CatchAction action) @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI return true; case CatchAction.Dash: - MovableCatcher.Dashing = true; + Catcher.Dashing = true; return true; } @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Catch.UI break; case CatchAction.Dash: - MovableCatcher.Dashing = false; + Catcher.Dashing = false; break; } } From a63065dbe9818115153a27fabeac958d4688dae6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:55:51 +0900 Subject: [PATCH 287/489] Tidy up `ModDisplay` --- .../UserInterface/TestSceneModDisplay.cs | 6 +-- .../TestSceneModSelectOverlay.cs | 1 - .../Screens/Play/BeatmapMetadataDisplay.cs | 1 - osu.Game/Screens/Play/HUD/ModDisplay.cs | 53 ++++++++----------- osu.Game/Screens/Play/HUDOverlay.cs | 1 - 5 files changed, 24 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs index 8168faa106..b8f5ee5e86 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs @@ -11,10 +11,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneModDisplay : OsuTestScene { - [TestCase(ExpansionMode.ExpandOnHover)] - [TestCase(ExpansionMode.AlwaysExpanded)] - [TestCase(ExpansionMode.AlwaysContracted)] - public void TestMode(ExpansionMode mode) + [Test] + public void TestMode([Values] ExpansionMode mode) { AddStep("create mod display", () => { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 3485d7fbc3..1e76c33fca 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -416,7 +416,6 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, Position = new Vector2(-5, 25), Current = { BindTarget = modSelect.SelectedMods } } diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index fd1150650c..4265a83ce1 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -172,7 +172,6 @@ namespace osu.Game.Screens.Play { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Top = 20 }, Current = mods }, diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 2f7ca74372..36127292c3 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -15,24 +15,26 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { - public class ModDisplay : Container, IHasCurrentValue> + /// + /// Displays a single-line horizontal auto-sized flow of mods. + /// + public class ModDisplay : CompositeDrawable, IHasCurrentValue> { private const int fade_duration = 1000; public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; - private readonly Bindable> current = new Bindable>(); + private readonly BindableWithCurrent> current = new BindableWithCurrent>(); public Bindable> Current { - get => current; + get => current.Current; set { if (value == null) throw new ArgumentNullException(nameof(value)); - current.UnbindBindings(); - current.BindTo(value); + current.Current = value; } } @@ -42,22 +44,10 @@ namespace osu.Game.Screens.Play.HUD { AutoSizeAxes = Axes.Both; - Child = new FillFlowContainer + InternalChild = iconsContainer = new ReverseChildIDFillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - iconsContainer = new ReverseChildIDFillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - }, - }, + Direction = FillDirection.Horizontal, }; } @@ -71,22 +61,23 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); - Current.BindValueChanged(mods => - { - iconsContainer.Clear(); - - if (mods.NewValue != null) - { - foreach (Mod mod in mods.NewValue) - iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) }); - - appearTransform(); - } - }, true); + Current.BindValueChanged(updateDisplay, true); iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint); } + private void updateDisplay(ValueChangedEvent> mods) + { + iconsContainer.Clear(); + + if (mods.NewValue == null) return; + + foreach (Mod mod in mods.NewValue) + iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) }); + + appearTransform(); + } + private void appearTransform() { expand(); diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index ffe03815f5..2cf2555b3e 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -282,7 +282,6 @@ namespace osu.Game.Screens.Play { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, }; protected PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From 30777795ce2caf73a9a4c8c3da19b79b5f589112 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:40:35 +0900 Subject: [PATCH 288/489] Add some doc comment to `CatcherArea` --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index f2c5f96b26..bc0c101805 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -15,6 +15,11 @@ using osuTK; namespace osu.Game.Rulesets.Catch.UI { + /// + /// The horizontal band at the bottom of the playfield the catcher is moving on. + /// It holds a as a child and translates input to the catcher movement. + /// It also holds a combo display that is above the catcher, and judgment results are translated to the catcher and the combo display. + /// public class CatcherArea : Container, IKeyBindingHandler { public const float CATCHER_SIZE = 106.75f; @@ -42,6 +47,9 @@ namespace osu.Game.Rulesets.Catch.UI /// private int currentDirection; + /// + /// must be set before loading. + /// public CatcherArea() { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); From 396ad79d5040a01bec292d034dcf92a0e05e9441 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:43:08 +0900 Subject: [PATCH 289/489] Remove unnecessary `UnbindAll` call --- osu.Game/Screens/Play/HUD/ModDisplay.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 36127292c3..2cf24a18dc 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -51,12 +51,6 @@ namespace osu.Game.Screens.Play.HUD }; } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - Current.UnbindAll(); - } - protected override void LoadComplete() { base.LoadComplete(); From d9f9ad35be19c7677ce32e49d68c5f1f1bb13fde Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:43:24 +0900 Subject: [PATCH 290/489] Move catcher base size constant to `Catcher`. --- .../Replays/CatchAutoGenerator.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 19 ++++++++++++------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 4 +--- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 2 +- .../UI/SkinnableCatcher.cs | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index a81703119a..2fc05701db 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Replays bool impossibleJump = speedRequired > movement_speed * 2; // todo: get correct catcher size, based on difficulty CS. - const float catcher_width_half = CatcherArea.CATCHER_SIZE * 0.3f * 0.5f; + const float catcher_width_half = Catcher.BASE_SIZE * 0.3f * 0.5f; if (lastPosition - catcher_width_half < h.EffectiveX && lastPosition + catcher_width_half > h.EffectiveX) { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a671bce590..49508b1caf 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -25,6 +25,16 @@ namespace osu.Game.Rulesets.Catch.UI { public class Catcher : SkinReloadableDrawable { + /// + /// The size of the catcher at 1x scale. + /// + public const float BASE_SIZE = 106.75f; + + /// + /// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable. + /// + public const float ALLOWED_CATCH_RANGE = 0.8f; + /// /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail /// and end glow/after-image during a hyper-dash. @@ -82,11 +92,6 @@ namespace osu.Game.Rulesets.Catch.UI private set => Body.AnimationState.Value = value; } - /// - /// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable. - /// - public const float ALLOWED_CATCH_RANGE = 0.8f; - private bool dashing; public bool Dashing @@ -140,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.UI Origin = Anchor.TopCentre; - Size = new Vector2(CatcherArea.CATCHER_SIZE); + Size = new Vector2(BASE_SIZE); if (difficulty != null) Scale = calculateScale(difficulty); @@ -197,7 +202,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Calculates the width of the area used for attempting catches in gameplay. /// /// The scale of the catcher. - public static float CalculateCatchWidth(Vector2 scale) => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * ALLOWED_CATCH_RANGE; + public static float CalculateCatchWidth(Vector2 scale) => BASE_SIZE * Math.Abs(scale.X) * ALLOWED_CATCH_RANGE; /// /// Calculates the width of the area used for attempting catches in gameplay. diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index bc0c101805..de0ace9817 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Catch.UI /// public class CatcherArea : Container, IKeyBindingHandler { - public const float CATCHER_SIZE = 106.75f; - public Catcher Catcher { get => catcher; @@ -52,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.UI /// public CatcherArea() { - Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); + Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE); Child = comboDisplay = new CatchComboDisplay { RelativeSizeAxes = Axes.None, diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index c961d98dc5..ff1a7d7a61 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.UI public CatcherTrail() { - Size = new Vector2(CatcherArea.CATCHER_SIZE); + Size = new Vector2(Catcher.BASE_SIZE); Origin = Anchor.TopCentre; Blending = BlendingParameters.Additive; InternalChild = body = new SkinnableCatcher diff --git a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs index fc34ba4c8b..8d707a4beb 100644 --- a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs +++ b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI { Anchor = Anchor.TopCentre; // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling. - OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE; + OriginPosition = new Vector2(0.5f, 0.06f) * Catcher.BASE_SIZE; } } } From 5bee06fd5f3c0ecb1c321c4b02ad42b1b62ece9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:43:29 +0900 Subject: [PATCH 291/489] Remove forgotten `AutoSize` specification --- .../Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 7e8dcdcfe0..3d67df86e4 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -135,7 +135,6 @@ namespace osu.Game.Screens.Ranking.Contracted { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, ExpansionMode = ExpansionMode.AlwaysExpanded, Current = { Value = score.Mods }, Scale = new Vector2(0.5f), From b65e607941d14432934f561260ff7eedeeefa24e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:35:59 +0900 Subject: [PATCH 292/489] Add test covering mod displays with too many mods --- .../Ranking/TestSceneContractedPanelMiddleContent.cs | 6 ++++++ .../Ranking/TestSceneExpandedPanelMiddleContent.cs | 11 +++++++++++ osu.Game/Tests/TestScoreInfo.cs | 8 ++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs index 76cfe75b59..acacdf8644 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs @@ -29,6 +29,12 @@ namespace osu.Game.Tests.Visual.Ranking AddStep("show example score", () => showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new TestScoreInfo(new OsuRuleset().RulesetInfo))); } + [Test] + public void TestExcessMods() + { + AddStep("show excess mods score", () => showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new TestScoreInfo(new OsuRuleset().RulesetInfo, true))); + } + private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score) { Child = new ContractedPanelMiddleContentContainer(workingBeatmap, score); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 591095252f..495c369eac 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -36,6 +36,17 @@ namespace osu.Game.Tests.Visual.Ranking { Beatmap = createTestBeatmap(author) })); + } + + [Test] + public void TestExcessMods() + { + var author = new User { Username = "mapper_name" }; + + AddStep("show excess mods score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo, true) + { + Beatmap = createTestBeatmap(author) + })); AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Current.Value == "mapper_name")); } diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs index 9090a12d3f..8ce71ace69 100644 --- a/osu.Game/Tests/TestScoreInfo.cs +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -13,7 +14,7 @@ namespace osu.Game.Tests { public class TestScoreInfo : ScoreInfo { - public TestScoreInfo(RulesetInfo ruleset) + public TestScoreInfo(RulesetInfo ruleset, bool excessMods = false) { User = new User { @@ -25,7 +26,10 @@ namespace osu.Game.Tests Beatmap = new TestBeatmap(ruleset).BeatmapInfo; Ruleset = ruleset; RulesetID = ruleset.ID ?? 0; - Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }; + + Mods = excessMods + ? ruleset.CreateInstance().GetAllMods().ToArray() + : new Mod[] { new TestModHardRock(), new TestModDoubleTime() }; TotalScore = 2845370; Accuracy = 0.95; From b910c212309feabfc5578aee96eee3dabc94c8a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:38:18 +0900 Subject: [PATCH 293/489] Add `ModFlowDisplay` and consume in `ContractedPanelMiddleContent` --- .../UserInterface/TestSceneModFlowDisplay.cs | 49 +++++++++++ osu.Game/Screens/Play/HUD/ModDisplay.cs | 2 +- osu.Game/Screens/Play/HUD/ModFlowDisplay.cs | 83 +++++++++++++++++++ .../ContractedPanelMiddleContent.cs | 7 +- 4 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs create mode 100644 osu.Game/Screens/Play/HUD/ModFlowDisplay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs new file mode 100644 index 0000000000..8f057c663b --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.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 System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneModFlowDisplay : OsuTestScene + { + private ModFlowDisplay modFlow; + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = modFlow = new ModFlowDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.None, + Width = 200, + Current = + { + Value = new OsuRuleset().GetAllMods().ToArray(), + } + }; + }); + + [Test] + public void TestWrapping() + { + AddSliderStep("icon size", 0.1f, 2, 1, val => + { + if (modFlow != null) + modFlow.IconScale = val; + }); + + AddSliderStep("flow width", 100, 500, 200, val => + { + if (modFlow != null) + modFlow.Width = val; + }); + } + } +} diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 2cf24a18dc..b4a3eb209a 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { /// - /// Displays a single-line horizontal auto-sized flow of mods. + /// Displays a single-line horizontal auto-sized flow of mods. For cases where wrapping is required, use instead. /// public class ModDisplay : CompositeDrawable, IHasCurrentValue> { diff --git a/osu.Game/Screens/Play/HUD/ModFlowDisplay.cs b/osu.Game/Screens/Play/HUD/ModFlowDisplay.cs new file mode 100644 index 0000000000..ff3ca6460f --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ModFlowDisplay.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; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// A horizontally wrapping display of mods. For cases where wrapping is not required, use instead. + /// + public class ModFlowDisplay : ReverseChildIDFillFlowContainer, IHasCurrentValue> + { + private const int fade_duration = 1000; + + private readonly BindableWithCurrent> current = new BindableWithCurrent>(); + + public Bindable> Current + { + get => current.Current; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + current.Current = value; + } + } + + private float iconScale = 1; + + public float IconScale + { + get => iconScale; + set + { + iconScale = value; + updateDisplay(); + } + } + + public ModFlowDisplay() + { + Direction = FillDirection.Full; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateDisplay(), true); + + this.FadeInFromZero(fade_duration, Easing.OutQuint); + } + + private void updateDisplay() + { + Clear(); + + if (current.Value == null) return; + + Spacing = new Vector2(0, -12 * iconScale); + + foreach (Mod mod in current.Value) + { + Add(new ModIcon(mod) + { + Scale = new Vector2(0.6f * iconScale), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); + } + } + } +} diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 3d67df86e4..20c603295b 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -131,13 +131,14 @@ namespace osu.Game.Screens.Ranking.Contracted createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"), } }, - new ModDisplay + new ModFlowDisplay { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - ExpansionMode = ExpansionMode.AlwaysExpanded, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Current = { Value = score.Mods }, - Scale = new Vector2(0.5f), + IconScale = 0.5f, } } } From 173334383f0cf60ed068db0067b67606f7f510a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:56:09 +0900 Subject: [PATCH 294/489] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 171a0862a1..da7a8209a4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 152ba55e08..0e6cee8f18 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 dc15df6ea6..aa4bec06e3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 399c3b0be88d2c30ce88b5fb0f17c7eb6c69644b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 17:32:56 +0900 Subject: [PATCH 295/489] Rename property, reword xmldoc and improve readability of update code --- .../Objects/CatchHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- .../Formats/LegacyBeatmapDecoderTest.cs | 24 +++++++++---------- .../TestSceneHitObjectAccentColour.cs | 2 +- osu.Game/Beatmaps/BeatmapProcessor.cs | 14 +++++------ .../Objects/Types/IHasComboInformation.cs | 6 ++--- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- 7 files changed, 25 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index bad558fcad..33353ec423 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Objects set => ComboIndexBindable.Value = value; } - public int BeatmapSkinComboIndex { get; set; } + public int ComboIndexWithOffsets { get; set; } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index db8a02aa0f..7c60480b00 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Objects set => ComboIndexBindable.Value = value; } - public int BeatmapSkinComboIndex { get; set; } + public int ComboIndexWithOffsets { get; set; } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 42f89a758e..4fe1cf3790 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -323,12 +323,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuBeatmapProcessor(converted).PreProcess(); new OsuBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); } } @@ -346,12 +346,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new CatchBeatmapProcessor(converted).PreProcess(); new CatchBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); } } diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index f6f482e254..9d53a5ba63 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Gameplay set => ComboIndexBindable.Value = value; } - public int BeatmapSkinComboIndex { get; set; } + public int ComboIndexWithOffsets { get; set; } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index 9121222bd7..cdeaab06ed 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -34,21 +34,19 @@ namespace osu.Game.Beatmaps 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 = (lastObj?.ComboIndex ?? 0) + 1; - obj.BeatmapSkinComboIndex = (lastObj?.BeatmapSkinComboIndex ?? 0) + obj.ComboOffset + 1; + obj.ComboIndex++; + obj.ComboIndexWithOffsets += obj.ComboOffset + 1; if (lastObj != null) lastObj.LastInCombo = true; } - else if (lastObj != null) - { - obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1; - obj.ComboIndex = lastObj.ComboIndex; - obj.BeatmapSkinComboIndex = lastObj.BeatmapSkinComboIndex; - } lastObj = obj; } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 46d7bef498..f01181f436 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -27,10 +27,10 @@ namespace osu.Game.Rulesets.Objects.Types int ComboIndex { get; set; } /// - /// A with the of this and all previous hitobjects applied to it. - /// This is used primarily for beatmap skins during combo colour retrieval, rather than the regular . + /// The offset of this combo in relation to the beatmap, with all aggregate s applied. + /// This should be used instead of only when retrieving combo colours from the beatmap's skin. /// - int BeatmapSkinComboIndex { get; set; } + int ComboIndexWithOffsets { get; set; } /// /// Whether the HitObject starts a new combo. diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 5b46a21381..e6ddeba316 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -63,7 +63,7 @@ namespace osu.Game.Skinning } protected override IBindable GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) - => base.GetComboColour(source, combo.BeatmapSkinComboIndex, combo); + => base.GetComboColour(source, combo.ComboIndexWithOffsets, combo); public override ISample GetSample(ISampleInfo sampleInfo) { From 0a950a5da74e94216440fb235e2019e20e278b33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 17:59:50 +0900 Subject: [PATCH 296/489] Disable auto property get-only inspection --- osu.sln.DotSettings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 7284ca1a9a..139ee02b96 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -19,8 +19,8 @@ HINT DO_NOT_SHOW WARNING - WARNING - WARNING + HINT + HINT WARNING WARNING WARNING From f85ff40a6b3eddcc4e9a0ae21853eed059759616 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Wed, 21 Jul 2021 11:47:21 +0200 Subject: [PATCH 297/489] Add back LeftMouse button check --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 06cbf7c2e0..a24084aa05 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // even if a selection didn't occur, a drag event may still move the selection. bool movementPossible = prepareSelectionMovement(); - return selectionPerformed || movementPossible; + return selectionPerformed || (e.Button == MouseButton.Left && movementPossible); } protected SelectionBlueprint ClickedBlueprint { get; private set; } From 9d43ca122fc52372d0c50ebe84789095def45d6b Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Wed, 21 Jul 2021 12:04:09 +0200 Subject: [PATCH 298/489] Allow context menus to be triggered as well --- .../Components/Timeline/TimelineBlueprintContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index a5210132a3..354311b7ff 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -238,6 +238,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler { + [Resolved] + private Timeline timeline { get; set; } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); + // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; From 032ced5d168075c427c4adf7bc8559f45a0aec02 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 21 Jul 2021 13:05:01 +0200 Subject: [PATCH 299/489] Localise beatmap explicit content pill. --- osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs index 329f8ee0a2..ba78592ed2 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { @@ -34,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapSet new OsuSpriteText { Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - Text = "EXPLICIT", + Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), Colour = OverlayColourProvider.Orange.Colour2, } From ea4f9b2ac772c7fd26d9075210dd5ce0502a26be Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 21 Jul 2021 13:15:07 +0200 Subject: [PATCH 300/489] Localise beatmap online status pill. --- osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs | 41 +++++++++++++++++++ .../Drawables/BeatmapSetOnlineStatusPill.cs | 4 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs index ae5a44cfcd..6003e23a84 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs @@ -1,8 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Beatmaps { + [LocalisableEnum(typeof(BeatmapSetOnlineStatusEnumLocalisationMapper))] public enum BeatmapSetOnlineStatus { None = -3, @@ -20,4 +25,40 @@ namespace osu.Game.Beatmaps public static bool GrantsPerformancePoints(this BeatmapSetOnlineStatus status) => status == BeatmapSetOnlineStatus.Ranked || status == BeatmapSetOnlineStatus.Approved; } + + public class BeatmapSetOnlineStatusEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(BeatmapSetOnlineStatus value) + { + switch (value) + { + case BeatmapSetOnlineStatus.None: + return string.Empty; + + case BeatmapSetOnlineStatus.Graveyard: + return BeatmapsetsStrings.ShowStatusGraveyard; + + case BeatmapSetOnlineStatus.WIP: + return BeatmapsetsStrings.ShowStatusWip; + + case BeatmapSetOnlineStatus.Pending: + return BeatmapsetsStrings.ShowStatusPending; + + case BeatmapSetOnlineStatus.Ranked: + return BeatmapsetsStrings.ShowStatusRanked; + + case BeatmapSetOnlineStatus.Approved: + return BeatmapsetsStrings.ShowStatusApproved; + + case BeatmapSetOnlineStatus.Qualified: + return BeatmapsetsStrings.ShowStatusQualified; + + case BeatmapSetOnlineStatus.Loved: + return BeatmapsetsStrings.ShowStatusLoved; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } } diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index f6e03d40ff..ffc010b3a3 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -1,6 +1,8 @@ // 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.Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -28,7 +30,7 @@ namespace osu.Game.Beatmaps.Drawables status = value; Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1; - statusText.Text = value.ToString().ToUpperInvariant(); + statusText.Text = value.GetLocalisableDescription().ToUpper(); } } From cd54653977e4486452f3cfbb6d81dc78828a7ad5 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 21 Jul 2021 22:00:13 +0900 Subject: [PATCH 301/489] Add 'Soft' HoverSampleSet variant --- osu.Game/Graphics/UserInterface/HoverSampleSet.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs index b88f81a143..b4afb4831f 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs @@ -10,6 +10,9 @@ namespace osu.Game.Graphics.UserInterface [Description("default")] Default, + [Description("soft")] + Soft, + [Description("button")] Button, From e3d1868af5cf49d730a26eff7cd522179a733645 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 21 Jul 2021 22:01:00 +0900 Subject: [PATCH 302/489] Add hover/select sounds to directory/file selector components --- .../UserInterfaceV2/OsuDirectorySelectorDirectory.cs | 9 +++++++-- osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs index 8a420cdcfb..794c728e56 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -25,9 +26,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 Flow.AutoSizeAxes = Axes.X; Flow.Height = OsuDirectorySelector.ITEM_HEIGHT; - AddInternal(new Background + AddRangeInternal(new Drawable[] { - Depth = 1 + new Background + { + Depth = 1 + }, + new HoverClickSounds(HoverSampleSet.Soft) }); } diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index b9fb642cbe..e4c78e723d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -50,9 +51,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 Flow.AutoSizeAxes = Axes.X; Flow.Height = OsuDirectorySelector.ITEM_HEIGHT; - AddInternal(new OsuDirectorySelectorDirectory.Background + AddRangeInternal(new Drawable[] { - Depth = 1 + new OsuDirectorySelectorDirectory.Background + { + Depth = 1 + }, + new HoverClickSounds(HoverSampleSet.Soft) }); } From 507b53dc73b5f97976c9d2289d0b3a7b2148923e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 21 Jul 2021 22:02:40 +0900 Subject: [PATCH 303/489] Use 'Soft' hover/select samples for EditorTable row selection --- osu.Game/Screens/Edit/EditorTable.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 815f3ed0ea..9578b96897 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -9,6 +9,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK.Graphics; @@ -64,6 +65,7 @@ namespace osu.Game.Screens.Edit private EditorClock clock { get; set; } public RowBackground(object item) + : base(HoverSampleSet.Soft) { Item = item; From d93bf5be8023db9d68b9dc3822ca93eb36dbcdad Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 12:58:39 +0900 Subject: [PATCH 304/489] Don't handle mouse down at fixed vertices --- .../Blueprints/Components/SelectionEditablePath.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 6c08b59e09..8c7314d0b6 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -44,8 +44,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override bool OnMouseDown(MouseDownEvent e) { int index = getMouseTargetVertex(e.ScreenSpaceMouseDownPosition); - - if (index == -1) + if (index == -1 || VertexStates[index].IsFixed) return false; if (e.Button == MouseButton.Left && e.ShiftPressed) @@ -65,7 +64,12 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override bool OnDragStart(DragStartEvent e) { - if (e.Button != MouseButton.Left || getMouseTargetVertex(e.ScreenSpaceMouseDownPosition) == -1) return false; + int index = getMouseTargetVertex(e.ScreenSpaceMouseDownPosition); + if (index == -1 || VertexStates[index].IsFixed) + return false; + + if (e.Button != MouseButton.Left) + return false; dragStartPosition = ToRelativePosition(e.ScreenSpaceMouseDownPosition); From aab7678a69029b0fe9e97d28b9cee0e585010567 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 22 Jul 2021 12:18:37 +0800 Subject: [PATCH 305/489] Truncate beatmap text --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 134 ++++++++++-------- 1 file changed, 75 insertions(+), 59 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index a3a61ccc36..69eb857661 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -108,7 +108,11 @@ namespace osu.Game.Screens.OnlinePlay difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); + beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text => + { + text.Truncate = true; + text.RelativeSizeAxes = Axes.X; + }); authorText.Clear(); @@ -147,84 +151,96 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both, Beatmap = { BindTarget = beatmap } }, - new FillFlowContainer + new GridContainer { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 8 }, - Spacing = new Vector2(8, 0), - Direction = FillDirection.Horizontal, - Children = new Drawable[] + ColumnDimensions = new[] { - difficultyIconContainer = new Container + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] + difficultyIconContainer = new Container { - beatmapText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, - new FillFlowContainer + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Left = 8, Right = 8, }, + }, + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0), - Children = new Drawable[] + beatmapText = new LinkFlowContainer(fontParameters) { - new FillFlowContainer + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0), + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0), - Children = new Drawable[] + new FillFlowContainer { - authorText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, - explicitContentPill = new ExplicitContentBeatmapPill + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0), + Children = new Drawable[] { - Alpha = 0f, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Top = 3f }, - } + authorText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, + explicitContentPill = new ExplicitContentBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Top = 3f }, + } + }, }, - }, - new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Child = modDisplay = new ModDisplay + new Container { - Scale = new Vector2(0.4f), - ExpansionMode = ExpansionMode.AlwaysExpanded + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Child = modDisplay = new ModDisplay + { + Scale = new Vector2(0.4f), + ExpansionMode = ExpansionMode.AlwaysExpanded + } } } } } + }, + new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = 8, Right = 10, }, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), + ChildrenEnumerable = CreateButtons().Select(button => button.With(b => + { + b.Anchor = Anchor.Centre; + b.Origin = Anchor.Centre; + })) } } } }, - new FillFlowContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5), - X = -10, - ChildrenEnumerable = CreateButtons().Select(button => button.With(b => - { - b.Anchor = Anchor.Centre; - b.Origin = Anchor.Centre; - })) - } } }; } From 7b6981c632d0f3b649c459f9cef67b7907863076 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 13:06:48 +0900 Subject: [PATCH 306/489] Don't show the flip button when flipping is a no-op --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 3 ++- osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 50290ae292..0262843c43 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -69,7 +69,8 @@ namespace osu.Game.Rulesets.Catch.Edit { base.OnSelectionChanged(); - SelectionBox.CanFlipX = true; + var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); + SelectionBox.CanFlipX = selectionRange.Length > 0 && EditorBeatmap.SelectedHitObjects.Any(h => h is CatchHitObject && !(h is BananaShower)); } /// diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs index 64e789483a..543e773582 100644 --- a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.Edit public readonly float Min; public readonly float Max; + public float Length => Max - Min; + public PositionRange(float value) : this(value, value) { From 19657cd00e461e4758ee6127edc61e2fa6f51b1f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 13:28:40 +0900 Subject: [PATCH 307/489] Guard against empty range in `PositionRange` --- osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs index 543e773582..e61603e5e6 100644 --- a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -8,14 +8,15 @@ using System; namespace osu.Game.Rulesets.Catch.Edit { /// - /// Represents a closed interval of horizontal positions in the playfield. + /// Represents either the empty range or a closed interval of horizontal positions in the playfield. + /// A represents a closed interval if it is <= , and represents the empty range otherwise. /// public readonly struct PositionRange { public readonly float Min; public readonly float Max; - public float Length => Max - Min; + public float Length => Math.Max(0, Max - Min); public PositionRange(float value) : this(value, value) @@ -30,7 +31,11 @@ namespace osu.Game.Rulesets.Catch.Edit public static PositionRange Union(PositionRange a, PositionRange b) => new PositionRange(Math.Min(a.Min, b.Min), Math.Max(a.Max, b.Max)); - public float GetFlippedPosition(float x) => Max - (x - Min); + /// + /// Get the given position flipped (mirrored) for the axis at the center of this range. + /// Returns the given position unchanged if the range was empty. + /// + public float GetFlippedPosition(float x) => Min <= Max ? Max - (x - Min) : x; public static readonly PositionRange EMPTY = new PositionRange(float.PositiveInfinity, float.NegativeInfinity); } From 2151c1863ee58a9832ab092bf2a2f14439ac9db3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:07:32 +0900 Subject: [PATCH 308/489] Rename variables for catch-specific casting to avoid any confusion --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 0262843c43..8593c452cf 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -40,12 +40,12 @@ namespace osu.Game.Rulesets.Catch.Edit EditorBeatmap.PerformOnSelection(h => { - if (!(h is CatchHitObject hitObject)) return; + if (!(h is CatchHitObject catchObject)) return; - hitObject.OriginalX += deltaX; + catchObject.OriginalX += deltaX; // Move the nested hit objects to give an instant result before nested objects are recreated. - foreach (var nested in hitObject.NestedHitObjects.OfType()) + foreach (var nested in catchObject.NestedHitObjects.OfType()) nested.OriginalX += deltaX; }); @@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Catch.Edit bool changed = false; EditorBeatmap.PerformOnSelection(h => { - if (h is CatchHitObject hitObject) - changed |= handleFlip(selectionRange, hitObject); + if (h is CatchHitObject catchObject) + changed |= handleFlip(selectionRange, catchObject); }); return changed; } From 57e5f5575a13bef84fd6c7fa5ffe99bbaac4cd81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:23:24 +0900 Subject: [PATCH 309/489] Fix derived API request types firing success when they shouldn't The usual case of `virtual`/`override` being dangerous when logic is added to the base implementation. As such, I've removed this completely. --- osu.Game/Online/API/APIDownloadRequest.cs | 11 +++++------ osu.Game/Online/API/APIRequest.cs | 13 ++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index 62e22d8f88..63bb3e2287 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -16,6 +16,11 @@ namespace osu.Game.Online.API /// protected virtual string FileExtension { get; } = @".tmp"; + protected APIDownloadRequest() + { + base.Success += () => Success?.Invoke(filename); + } + protected override WebRequest CreateWebRequest() { var file = Path.GetTempFileName(); @@ -39,12 +44,6 @@ namespace osu.Game.Online.API TriggerSuccess(); } - internal override void TriggerSuccess() - { - base.TriggerSuccess(); - Success?.Invoke(filename); - } - public event APIProgressHandler Progressed; public new event APISuccessHandler Success; diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 8d816d3975..e6bfca166e 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -25,6 +25,11 @@ namespace osu.Game.Online.API /// public new event APISuccessHandler Success; + protected APIRequest() + { + base.Success += () => Success?.Invoke(Result); + } + protected override void PostProcess() { base.PostProcess(); @@ -40,12 +45,6 @@ namespace osu.Game.Online.API TriggerSuccess(); } - - internal override void TriggerSuccess() - { - base.TriggerSuccess(); - Success?.Invoke(Result); - } } /// @@ -132,7 +131,7 @@ namespace osu.Game.Online.API { } - internal virtual void TriggerSuccess() + internal void TriggerSuccess() { lock (completionStateLock) { From ec3ce57bb9278d162564db366a1b0619b4f78a4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:33:05 +0900 Subject: [PATCH 310/489] Fix song select background not showing in multiplayer/playlists The screen was now being loaded against incorrect dependencies. I'm not sure why I thought it wasn't possible to just do the `Push` in `LoadComplete` as it seems to work without issue this time... Closes #13974. --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 25b02e5084..117ceab6f2 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -162,10 +162,6 @@ namespace osu.Game.Screens.OnlinePlay ongoingOperationTracker, } }; - - // a lot of the functionality in this class depends on loungeSubScreen being in a ready to go state. - // as such, we intentionally load this inline so it is ready alongside this screen. - LoadComponent(loungeSubScreen = CreateLounge()); } private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => @@ -184,7 +180,7 @@ namespace osu.Game.Screens.OnlinePlay screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; - screenStack.Push(loungeSubScreen); + screenStack.Push(loungeSubScreen = CreateLounge()); apiState.BindTo(API.State); apiState.BindValueChanged(onlineStateChanged, true); From db6f32326678f163cfd6d2dc3df96246ec22105d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:45:56 +0900 Subject: [PATCH 311/489] Output startup component load start/end times to non-debug logs Useful for diagnosing issues in cases like #13981. --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8119df43ac..a87361e33c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -932,7 +932,7 @@ namespace osu.Game try { - Logger.Log($"Loading {component}...", level: LogLevel.Debug); + Logger.Log($"Loading {component}..."); // Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true @@ -952,7 +952,7 @@ namespace osu.Game await task.ConfigureAwait(false); - Logger.Log($"Loaded {component}!", level: LogLevel.Debug); + Logger.Log($"Loaded {component}!"); } catch (OperationCanceledException) { From 0cfa8f0f5beb8c3efbf6c2722251c624c85f57f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:35:35 +0900 Subject: [PATCH 312/489] Update resources --- 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 da7a8209a4..69a89c3cd0 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0e6cee8f18..9825d29405 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index aa4bec06e3..3f81b36216 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From b5cc9010de97d5d81d608faf155d8fcd1a864b93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:39:01 +0900 Subject: [PATCH 313/489] Move resolved property to top of class --- .../Components/Timeline/TimelineBlueprintContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 354311b7ff..184bec7d44 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -190,6 +190,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private class SelectableAreaBackground : CompositeDrawable { + [Resolved] + private OsuColour colours { get; set; } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) { float localY = ToLocalSpace(screenSpacePos).Y; @@ -220,9 +223,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } - [Resolved] - private OsuColour colours { get; set; } - protected override bool OnHover(HoverEvent e) { this.FadeColour(colours.BlueLighter, 120, Easing.OutQuint); From 2beef89c23a779a12ded341bdb84e1f3094904e3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:20:33 +0900 Subject: [PATCH 314/489] Add empty juice stream placement blueprint (no implementation) --- .../JuiceStreamPlacementBlueprint.cs | 11 +++++++++ .../Edit/CatchHitObjectComposer.cs | 1 + .../Edit/JuiceStreamCompositionTool.cs | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs new file mode 100644 index 0000000000..0fb8dc32a2 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints +{ + public class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint + { + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index d360274aa6..050c2f625d 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Catch.Edit protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { new FruitCompositionTool(), + new JuiceStreamCompositionTool(), new BananaShowerCompositionTool() }; diff --git a/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs b/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs new file mode 100644 index 0000000000..cb66e2952e --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; + +namespace osu.Game.Rulesets.Catch.Edit +{ + public class JuiceStreamCompositionTool : HitObjectCompositionTool + { + public JuiceStreamCompositionTool() + : base(nameof(JuiceStream)) + { + } + + public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders); + + public override PlacementBlueprint CreatePlacementBlueprint() => new JuiceStreamPlacementBlueprint(); + } +} From 64102d297237c203c86ae107ed75f73ef498f036 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:41:01 +0900 Subject: [PATCH 315/489] Add initial implementation of juice stream placement --- .../Components/PlacementEditablePath.cs | 44 +++++++ .../JuiceStreamPlacementBlueprint.cs | 107 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs new file mode 100644 index 0000000000..13dfe9db54 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Rulesets.Catch.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class PlacementEditablePath : EditablePath + { + private JuiceStreamPathVertex originalNewVertex; + + public PlacementEditablePath(Func positionToDistance) + : base(positionToDistance) + { + } + + public void AddNewVertex() + { + var endVertex = Vertices[^1]; + int index = AddVertex(endVertex.Distance, endVertex.X); + + for (int i = 0; i < VertexCount; i++) + { + VertexStates[i].IsSelected = i == index; + VertexStates[i].VertexBeforeChange = Vertices[i]; + } + + originalNewVertex = Vertices[index]; + } + + /// + /// Move the vertex added by in the last time. + /// + public void MoveLastVertex(Vector2 screenSpacePosition) + { + Vector2 position = ToRelativePosition(screenSpacePosition); + double distanceDelta = PositionToDistance(position.Y) - originalNewVertex.Distance; + float xDelta = position.X - originalNewVertex.X; + MoveSelectedVertices(distanceDelta, xDelta); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 0fb8dc32a2..32ab1f19c1 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -1,11 +1,118 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit.Blueprints { public class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint { + private readonly ScrollingPath scrollingPath; + + private readonly NestedOutlineContainer nestedOutlineContainer; + + private readonly PlacementEditablePath editablePath; + + private int lastEditablePathId = -1; + + public JuiceStreamPlacementBlueprint() + { + InternalChildren = new Drawable[] + { + scrollingPath = new ScrollingPath(), + nestedOutlineContainer = new NestedOutlineContainer(), + editablePath = new PlacementEditablePath(positionToDistance) + }; + } + + protected override void Update() + { + base.Update(); + + if (PlacementActive == PlacementState.Active) + editablePath.UpdateFrom(HitObjectContainer, HitObject); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + switch (PlacementActive) + { + case PlacementState.Waiting: + if (e.Button != MouseButton.Left) break; + + editablePath.AddNewVertex(); + BeginPlacement(true); + return true; + + case PlacementState.Active: + switch (e.Button) + { + case MouseButton.Left: + editablePath.AddNewVertex(); + return true; + + case MouseButton.Right: + EndPlacement(HitObject.Duration > 0); + return true; + } + + break; + } + + return base.OnMouseDown(e); + } + + public override void UpdateTimeAndPosition(SnapResult result) + { + switch (PlacementActive) + { + case PlacementState.Waiting: + if (!(result.Time is double snappedTime)) return; + + HitObject.OriginalX = ToLocalSpace(result.ScreenSpacePosition).X; + HitObject.StartTime = snappedTime; + break; + + case PlacementState.Active: + Vector2 unsnappedPosition = GetContainingInputManager().CurrentState.Mouse.Position; + editablePath.MoveLastVertex(unsnappedPosition); + break; + + default: + return; + } + + // Make sure the up-to-date position is used for outlines. + Vector2 startPosition = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + editablePath.Position = nestedOutlineContainer.Position = scrollingPath.Position = startPosition; + + updateHitObjectFromPath(); + } + + private void updateHitObjectFromPath() + { + if (lastEditablePathId == editablePath.PathId) + return; + + editablePath.UpdateHitObjectFromPath(HitObject); + ApplyDefaultsToHitObject(); + + scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject); + nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject); + + lastEditablePathId = editablePath.PathId; + } + + private double positionToDistance(float relativeYPosition) + { + double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime); + return (time - HitObject.StartTime) * HitObject.Velocity; + } } } From 4e9ac5dc7b108234460050c22e2c986d9e4638b8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:46:23 +0900 Subject: [PATCH 316/489] Add tests of juice stream placement blueprint --- .../TestSceneJuiceStreamPlacementBlueprint.cs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs new file mode 100644 index 0000000000..02861b1adf --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -0,0 +1,146 @@ +// 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.Extensions.ObjectExtensions; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK.Input; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class TestSceneJuiceStreamPlacementBlueprint : CatchPlacementBlueprintTestScene + { + private const double velocity = 0.5; + + private JuiceStream lastObject => LastObject?.HitObject as JuiceStream; + + [BackgroundDependencyLoader] + private void load() + { + Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderTickRate = 5; + Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity * 10; + } + + [Test] + public void TestBasicPlacement() + { + double[] times = { 300, 800 }; + float[] positions = { 100, 200 }; + addPlacementSteps(times, positions); + + AddAssert("juice stream is placed", () => lastObject != null); + AddAssert("start time is correct", () => Precision.AlmostEquals(lastObject.StartTime, times[0])); + AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1])); + AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0])); + AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1])); + } + + [Test] + public void TestEmptyNotCommitted() + { + addMoveAndClickSteps(100, 100); + addMoveAndClickSteps(100, 100); + addMoveAndClickSteps(100, 100, true); + AddAssert("juice stream not placed", () => lastObject == null); + } + + [Test] + public void TestMultipleSegments() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 100, 150, 100, 100 }; + addPlacementSteps(times, positions); + + AddAssert("has 4 vertices", () => lastObject.Path.ControlPoints.Count == 4); + addPathCheckStep(times, positions); + } + + [Test] + public void TestVelocityLimit() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 200, 300, 100 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 200, 200, 100 }); + } + + [Test] + public void TestClampedPositionIsRestored() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 200, 200, 0, 250 }; + + addMoveAndClickSteps(times[0], positions[0]); + addMoveAndClickSteps(times[1], positions[1]); + AddMoveStep(times[2], positions[2]); + addMoveAndClickSteps(times[2], positions[3], true); + + addPathCheckStep(times, new float[] { 200, 200, 250 }); + } + + [Test] + public void TestFirstVertexIsFixed() + { + double[] times = { 100, 200 }; + float[] positions = { 100, 300 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 100, 150 }); + } + + [Test] + public void TestOutOfOrder() + { + double[] times = { 100, 700, 500, 300 }; + float[] positions = { 100, 200, 150, 50 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, positions); + } + + [Test] + public void TestMoveBeforeFirstVertex() + { + double[] times = { 300, 500, 100 }; + float[] positions = { 100, 100, 100 }; + addPlacementSteps(times, positions); + AddAssert("start time is correct", () => Precision.AlmostEquals(lastObject.StartTime, times[0])); + AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1], 1e-3)); + } + + protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableJuiceStream((JuiceStream)hitObject); + + protected override PlacementBlueprint CreateBlueprint() => new JuiceStreamPlacementBlueprint(); + + private void addMoveAndClickSteps(double time, float position, bool end = false) + { + AddMoveStep(time, position); + AddClickStep(end ? MouseButton.Right : MouseButton.Left); + } + + private void addPlacementSteps(double[] times, float[] positions) + { + for (int i = 0; i < times.Length; i++) + addMoveAndClickSteps(times[i], positions[i], i == times.Length - 1); + } + + private void addPathCheckStep(double[] times, float[] positions) => AddStep("assert path is correct", () => + Assert.That(getPositions(times), Is.EqualTo(positions).Within(Precision.FLOAT_EPSILON))); + + private float[] getPositions(IEnumerable times) + { + JuiceStream hitObject = lastObject.AsNonNull(); + return times + .Select(time => (time - hitObject.StartTime) * hitObject.Velocity) + .Select(distance => hitObject.EffectiveX + hitObject.Path.PositionAt(distance / hitObject.Distance).X) + .ToArray(); + } + } +} From 957a0686ed7625779f4db162d043b2ebbb2e0218 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:48:07 +0900 Subject: [PATCH 317/489] Split out nested classes from `TimelineBlueprintContainer` They got too big. --- .../Timeline/TimelineBlueprintContainer.cs | 121 ------------------ .../Components/Timeline/TimelineDragBox.cs | 79 ++++++++++++ .../Timeline/TimelineSelectionHandler.cs | 65 ++++++++++ 3 files changed, 144 insertions(+), 121 deletions(-) create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 184bec7d44..73c38ba23f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -13,11 +13,9 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -236,125 +234,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler - { - [Resolved] - private Timeline timeline { get; set; } - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); - - // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation - public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.EditorNudgeLeft: - nudgeSelection(-1); - return true; - - case GlobalAction.EditorNudgeRight: - nudgeSelection(1); - return true; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { - } - - /// - /// Nudge the current selection by the specified multiple of beat divisor lengths, - /// based on the timing at the first object in the selection. - /// - /// The direction and count of beat divisor lengths to adjust. - private void nudgeSelection(int amount) - { - var selected = EditorBeatmap.SelectedHitObjects; - - if (selected.Count == 0) - return; - - var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(selected.First().StartTime); - double adjustment = timingPoint.BeatLength / EditorBeatmap.BeatDivisor * amount; - - EditorBeatmap.PerformOnSelection(h => - { - h.StartTime += adjustment; - EditorBeatmap.Update(h); - }); - } - } - - private class TimelineDragBox : DragBox - { - // the following values hold the start and end X positions of the drag box in the timeline's local space, - // but with zoom unapplied in order to be able to compensate for positional changes - // while the timeline is being zoomed in/out. - private float? selectionStart; - private float selectionEnd; - - [Resolved] - private Timeline timeline { get; set; } - - public TimelineDragBox(Action performSelect) - : base(performSelect) - { - } - - protected override Drawable CreateBox() => new Box - { - RelativeSizeAxes = Axes.Y, - Alpha = 0.3f - }; - - public override bool HandleDrag(MouseButtonEvent e) - { - // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. - float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y; - if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY) - return false; - - selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; - - // only calculate end when a transition is not in progress to avoid bouncing. - if (Precision.AlmostEquals(timeline.CurrentZoom, timeline.Zoom)) - selectionEnd = e.MousePosition.X / timeline.CurrentZoom; - - updateDragBoxPosition(); - return true; - } - - private void updateDragBoxPosition() - { - if (selectionStart == null) - return; - - float rescaledStart = selectionStart.Value * timeline.CurrentZoom; - float rescaledEnd = selectionEnd * timeline.CurrentZoom; - - Box.X = Math.Min(rescaledStart, rescaledEnd); - Box.Width = Math.Abs(rescaledStart - rescaledEnd); - - var boxScreenRect = Box.ScreenSpaceDrawQuad.AABBFloat; - - // we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment. - boxScreenRect.Y -= boxScreenRect.Height; - boxScreenRect.Height *= 2; - - PerformSelection?.Invoke(boxScreenRect); - } - - public override void Hide() - { - base.Hide(); - selectionStart = null; - } - } - protected class TimelineSelectionBlueprintContainer : Container> { protected override Container> Content { get; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs new file mode 100644 index 0000000000..8aad8aa6dc --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Framework.Utils; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + public class TimelineDragBox : DragBox + { + // the following values hold the start and end X positions of the drag box in the timeline's local space, + // but with zoom unapplied in order to be able to compensate for positional changes + // while the timeline is being zoomed in/out. + private float? selectionStart; + private float selectionEnd; + + [Resolved] + private Timeline timeline { get; set; } + + public TimelineDragBox(Action performSelect) + : base(performSelect) + { + } + + protected override Drawable CreateBox() => new Box + { + RelativeSizeAxes = Axes.Y, + Alpha = 0.3f + }; + + public override bool HandleDrag(MouseButtonEvent e) + { + // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. + float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y; + if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY) + return false; + + selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; + + // only calculate end when a transition is not in progress to avoid bouncing. + if (Precision.AlmostEquals(timeline.CurrentZoom, timeline.Zoom)) + selectionEnd = e.MousePosition.X / timeline.CurrentZoom; + + updateDragBoxPosition(); + return true; + } + + private void updateDragBoxPosition() + { + if (selectionStart == null) + return; + + float rescaledStart = selectionStart.Value * timeline.CurrentZoom; + float rescaledEnd = selectionEnd * timeline.CurrentZoom; + + Box.X = Math.Min(rescaledStart, rescaledEnd); + Box.Width = Math.Abs(rescaledStart - rescaledEnd); + + var boxScreenRect = Box.ScreenSpaceDrawQuad.AABBFloat; + + // we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment. + boxScreenRect.Y -= boxScreenRect.Height; + boxScreenRect.Height *= 2; + + PerformSelection?.Invoke(boxScreenRect); + } + + public override void Hide() + { + base.Hide(); + selectionStart = null; + } + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs new file mode 100644 index 0000000000..354013a5fd --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler + { + [Resolved] + private Timeline timeline { get; set; } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); + + // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation + public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.EditorNudgeLeft: + nudgeSelection(-1); + return true; + + case GlobalAction.EditorNudgeRight: + nudgeSelection(1); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } + + /// + /// Nudge the current selection by the specified multiple of beat divisor lengths, + /// based on the timing at the first object in the selection. + /// + /// The direction and count of beat divisor lengths to adjust. + private void nudgeSelection(int amount) + { + var selected = EditorBeatmap.SelectedHitObjects; + + if (selected.Count == 0) + return; + + var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(selected.First().StartTime); + double adjustment = timingPoint.BeatLength / EditorBeatmap.BeatDivisor * amount; + + EditorBeatmap.PerformOnSelection(h => + { + h.StartTime += adjustment; + EditorBeatmap.Update(h); + }); + } + } +} From 3fd8de3b912c9b8db588ddeef75b1beccc485d8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:57:47 +0900 Subject: [PATCH 318/489] Fix skin editor's fake overlay potentially getting into a bad state --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 34fac9c9cf..1aedd90817 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -59,14 +59,13 @@ namespace osu.Game.Skinning.Editor public override void Hide() { - base.Hide(); + // base call intentionally omitted. skinEditor.Hide(); } public override void Show() { - base.Show(); - + // base call intentionally omitted. if (skinEditor == null) { LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); From 21053381c7046d5de3572c837d42394df9bc5e95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:59:00 +0900 Subject: [PATCH 319/489] Fix skin editor potentially eating `GlobalAction.Back` when not displayed --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 1aedd90817..2562e9c57c 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -37,8 +37,10 @@ namespace osu.Game.Skinning.Editor switch (action) { case GlobalAction.Back: - if (skinEditor?.State.Value == Visibility.Visible) - Hide(); + if (skinEditor?.State.Value != Visibility.Visible) + break; + + Hide(); return true; case GlobalAction.ToggleSkinEditor: From cc01b9e639f3542fc5ed6633aa6f75d765c0e50e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 16:14:43 +0900 Subject: [PATCH 320/489] Extract `SliderPath` reverse logic to be used in other rulesets --- .../Edit/OsuSelectionHandler.cs | 28 +---------- .../Rulesets/Objects/SliderPathExtensions.cs | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+), 26 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/SliderPathExtensions.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index c2c2226af0..358a44e0e6 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -76,32 +76,8 @@ namespace osu.Game.Rulesets.Osu.Edit if (h is Slider slider) { - var points = slider.Path.ControlPoints.ToArray(); - Vector2 endPos = points.Last().Position.Value; - - slider.Path.ControlPoints.Clear(); - - slider.Position += endPos; - - PathType? lastType = null; - - for (var i = 0; i < points.Length; i++) - { - var p = points[i]; - p.Position.Value -= endPos; - - // propagate types forwards to last null type - if (i == points.Length - 1) - p.Type.Value = lastType; - else if (p.Type.Value != null) - { - var newType = p.Type.Value; - p.Type.Value = lastType; - lastType = newType; - } - - slider.Path.ControlPoints.Insert(0, p); - } + slider.Path.Reverse(out Vector2 offset); + slider.Position += offset; } } diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs new file mode 100644 index 0000000000..1438c2f128 --- /dev/null +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +#nullable enable + +namespace osu.Game.Rulesets.Objects +{ + public static class SliderPathExtensions + { + /// + /// Reverse the direction of this path. + /// + /// The . + /// The positional offset of the resulting path. It should be added to the start position of this path. + public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset) + { + var points = sliderPath.ControlPoints.ToArray(); + positionalOffset = points.Last().Position.Value; + + sliderPath.ControlPoints.Clear(); + + PathType? lastType = null; + + for (var i = 0; i < points.Length; i++) + { + var p = points[i]; + p.Position.Value -= positionalOffset; + + // propagate types forwards to last null type + if (i == points.Length - 1) + p.Type.Value = lastType; + else if (p.Type.Value != null) + { + var newType = p.Type.Value; + p.Type.Value = lastType; + lastType = newType; + } + + sliderPath.ControlPoints.Insert(0, p); + } + } + } +} From dc90e4d24c0bcf986b0e7246d2758e43c6d91049 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 16:52:47 +0900 Subject: [PATCH 321/489] `EditorBeatmap.SelectedHitObjects` -> `SelectedItems` (same thing) --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 8593c452cf..ddae539361 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Edit Vector2 targetPosition = HitObjectContainer.ToLocalSpace(blueprint.ScreenSpaceSelectionPoint + moveEvent.ScreenSpaceDelta); float deltaX = targetPosition.X - originalPosition.X; - deltaX = limitMovement(deltaX, EditorBeatmap.SelectedHitObjects); + deltaX = limitMovement(deltaX, SelectedItems); if (deltaX == 0) { @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Catch.Edit public override bool HandleFlip(Direction direction) { - var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); + var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); bool changed = false; EditorBeatmap.PerformOnSelection(h => @@ -69,8 +69,8 @@ namespace osu.Game.Rulesets.Catch.Edit { base.OnSelectionChanged(); - var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); - SelectionBox.CanFlipX = selectionRange.Length > 0 && EditorBeatmap.SelectedHitObjects.Any(h => h is CatchHitObject && !(h is BananaShower)); + var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); + SelectionBox.CanFlipX = selectionRange.Length > 0 && SelectedItems.Any(h => h is CatchHitObject && !(h is BananaShower)); } /// From 9fff304554dcf26514a2ebdfdd47449005437fb3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 17:00:08 +0900 Subject: [PATCH 322/489] Implement reversing of selected pattern in catch editor --- .../Edit/CatchSelectionHandler.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index ddae539361..36072d7fcb 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -65,12 +65,33 @@ namespace osu.Game.Rulesets.Catch.Edit return changed; } + public override bool HandleReverse() + { + double selectionStartTime = SelectedItems.Min(h => h.StartTime); + double selectionEndTime = SelectedItems.Max(h => h.GetEndTime()); + + EditorBeatmap.PerformOnSelection(hitObject => + { + hitObject.StartTime = selectionEndTime - (hitObject.GetEndTime() - selectionStartTime); + + if (hitObject is JuiceStream juiceStream) + { + juiceStream.Path.Reverse(out Vector2 positionalOffset); + juiceStream.OriginalX += positionalOffset.X; + juiceStream.LegacyConvertedY += positionalOffset.Y; + EditorBeatmap.Update(juiceStream); + } + }); + return true; + } + protected override void OnSelectionChanged() { base.OnSelectionChanged(); var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); SelectionBox.CanFlipX = selectionRange.Length > 0 && SelectedItems.Any(h => h is CatchHitObject && !(h is BananaShower)); + SelectionBox.CanReverse = SelectedItems.Count > 1 || SelectedItems.Any(h => h is JuiceStream); } /// From cd7b90363afe3b39d923a70c054472a502d3f76d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 22 Jul 2021 16:15:23 +0300 Subject: [PATCH 323/489] Check nested hitobjects while asserting accent colour --- .../TestSceneLegacyBeatmapSkin.cs | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index f2f68e805c..f6484886da 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -9,6 +9,8 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; @@ -113,31 +115,43 @@ namespace osu.Game.Rulesets.Osu.Tests { int index = 0; - foreach (var drawable in TestPlayer.DrawableRuleset.Playfield.AllHitObjects) + return TestPlayer.DrawableRuleset.Playfield.AllHitObjects.All(d => { - index = nextExpectedComboIndex(index, (OsuHitObject)drawable.HitObject); - - if (drawable.AccentColour.Value != expectedColours[index % expectedColours.Length]) - return false; - } - - return true; + index = nextExpectedComboIndex(index, (OsuHitObject)d.HitObject); + return checkComboColour(d, expectedColours[index % expectedColours.Length]); + }); }); + + static bool checkComboColour(DrawableHitObject drawableHitObject, Color4 expectedColour) + { + return drawableHitObject.AccentColour.Value == expectedColour && + drawableHitObject.NestedHitObjects.All(n => checkComboColour(n, expectedColour)); + } } private static IEnumerable getHitCirclesWithLegacyOffsets() { var hitObjects = new List(); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 10; i++) { - hitObjects.Add(new HitCircle - { - StartTime = i, - Position = new Vector2(256, 192), - NewCombo = true, - ComboOffset = i, - }); + var hitObject = i % 2 == 0 + ? (OsuHitObject)new HitCircle() + : new Slider() + { + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0)), + new PathControlPoint(new Vector2(100, 0)), + }) + }; + + hitObject.StartTime = i; + hitObject.Position = new Vector2(256, 192); + hitObject.NewCombo = true; + hitObject.ComboOffset = i; + + hitObjects.Add(hitObject); } return hitObjects; From 523c154f153f0bb2e692a7048d68e3977f8dfdfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 22 Jul 2021 16:22:42 +0300 Subject: [PATCH 324/489] Add `ComboIndexWithOffsetsBindable` and bind similar to `ComboIndexBindable` --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 8 +++++++- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 8 +++++++- .../Gameplay/TestSceneHitObjectAccentColour.cs | 8 +++++++- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 11 +++++++++++ osu.Game/Rulesets/Objects/HitObject.cs | 1 + .../Rulesets/Objects/Types/IHasComboInformation.cs | 2 ++ .../Components/Timeline/TimelineHitObjectBlueprint.cs | 6 ++++++ 7 files changed, 41 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 33353ec423..d43e6f1c8b 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -95,7 +95,13 @@ namespace osu.Game.Rulesets.Catch.Objects set => ComboIndexBindable.Value = value; } - public int ComboIndexWithOffsets { get; set; } + public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable(); + + public int ComboIndexWithOffsets + { + get => ComboIndexWithOffsetsBindable.Value; + set => ComboIndexWithOffsetsBindable.Value = value; + } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 7c60480b00..36629fa41e 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -97,7 +97,13 @@ namespace osu.Game.Rulesets.Osu.Objects set => ComboIndexBindable.Value = value; } - public int ComboIndexWithOffsets { get; set; } + public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable(); + + public int ComboIndexWithOffsets + { + get => ComboIndexWithOffsetsBindable.Value; + set => ComboIndexWithOffsetsBindable.Value = value; + } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 9d53a5ba63..34f70659e3 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -100,7 +100,13 @@ namespace osu.Game.Tests.Gameplay set => ComboIndexBindable.Value = value; } - public int ComboIndexWithOffsets { get; set; } + public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable(); + + public int ComboIndexWithOffsets + { + get => ComboIndexWithOffsetsBindable.Value; + set => ComboIndexWithOffsetsBindable.Value = value; + } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 69bdb5fd73..2b222371e2 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -124,7 +124,9 @@ namespace osu.Game.Rulesets.Objects.Drawables public readonly Bindable StartTimeBindable = new Bindable(); private readonly BindableList samplesBindable = new BindableList(); private readonly Bindable userPositionalHitSounds = new Bindable(); + private readonly Bindable comboIndexBindable = new Bindable(); + private readonly Bindable comboIndexWithOffsetsBindable = new Bindable(); protected override bool RequiresChildrenUpdate => true; @@ -186,6 +188,7 @@ namespace osu.Game.Rulesets.Objects.Drawables base.LoadComplete(); comboIndexBindable.BindValueChanged(_ => UpdateComboColour(), true); + comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); updateState(ArmedState.Idle, true); } @@ -250,7 +253,10 @@ namespace osu.Game.Rulesets.Objects.Drawables StartTimeBindable.BindValueChanged(onStartTimeChanged); if (HitObject is IHasComboInformation combo) + { comboIndexBindable.BindTo(combo.ComboIndexBindable); + comboIndexWithOffsetsBindable.BindTo(combo.ComboIndexWithOffsetsBindable); + } samplesBindable.BindTo(HitObject.SamplesBindable); samplesBindable.BindCollectionChanged(onSamplesChanged, true); @@ -275,8 +281,13 @@ namespace osu.Game.Rulesets.Objects.Drawables protected sealed override void OnFree(HitObjectLifetimeEntry entry) { StartTimeBindable.UnbindFrom(HitObject.StartTimeBindable); + if (HitObject is IHasComboInformation combo) + { comboIndexBindable.UnbindFrom(combo.ComboIndexBindable); + comboIndexWithOffsetsBindable.UnbindFrom(combo.ComboIndexWithOffsetsBindable); + } + samplesBindable.UnbindFrom(HitObject.SamplesBindable); // Changes in start time trigger state updates. When a new hitobject is applied, OnApply() automatically performs a state update anyway. diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index db02eafa92..422655502d 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -118,6 +118,7 @@ namespace osu.Game.Rulesets.Objects foreach (var n in NestedHitObjects.OfType()) { n.ComboIndexBindable.BindTo(hasCombo.ComboIndexBindable); + n.ComboIndexWithOffsetsBindable.BindTo(hasCombo.ComboIndexWithOffsetsBindable); n.IndexInCurrentComboBindable.BindTo(hasCombo.IndexInCurrentComboBindable); } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index f01181f436..eddf764ae7 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Objects.Types /// int ComboIndex { get; set; } + Bindable ComboIndexWithOffsetsBindable { get; } + /// /// The offset of this combo in relation to the beatmap, with all aggregate s applied. /// This should be used instead of only when retrieving combo colours from the beatmap's skin. diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 7f8cc1c8fa..d115c10b5d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -37,7 +37,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly Bindable startTime; private Bindable indexInCurrentComboBindable; + private Bindable comboIndexBindable; + private Bindable comboIndexWithOffsetsBindable; + private Bindable displayColourBindable; private readonly ExtendableCircle circle; @@ -122,6 +125,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy(); comboIndexBindable.BindValueChanged(_ => updateColour(), true); + comboIndexWithOffsetsBindable = comboInfo.ComboIndexWithOffsetsBindable.GetBoundCopy(); + comboIndexWithOffsetsBindable.BindValueChanged(_ => updateColour(), true); + skin.SourceChanged += updateColour; break; } From 20adfabc975f39c7afbbd1ae622612a01395392b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 22 Jul 2021 16:42:00 +0300 Subject: [PATCH 325/489] Remove unnecessary parentheses I... forgot to amend that. --- osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index f6484886da..8b51225e98 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.Tests { var hitObject = i % 2 == 0 ? (OsuHitObject)new HitCircle() - : new Slider() + : new Slider { Path = new SliderPath(new[] { From 00ec229bdec5eba0791a634c540611313dba148f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 22 Jul 2021 19:20:20 +0200 Subject: [PATCH 326/489] Localise stat values according to the current locale. --- .../Overlays/Profile/Header/TopHeaderContainer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index b64dba62e3..438f52a2ce 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -181,19 +181,19 @@ namespace osu.Game.Overlays.Profile.Header if (user?.Statistics != null) { - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToLocalisableString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsHitAccuracy, user.Statistics.DisplayAccuracy)); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToLocalisableString("#,##0"))); } } private class UserStatsLine : Container { - public UserStatsLine(LocalisableString left, string right) + public UserStatsLine(LocalisableString left, LocalisableString right) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From a5736085a9408fe7ca2bc9c927c989760b205a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 02:23:37 +0900 Subject: [PATCH 327/489] Ensure externally run operations on `LoungeSubScreen` are run after load is completed --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 27 +++++++++++-------- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 6 ++--- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f43109c4fa..68bd3cd613 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -177,7 +177,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge this.HidePopover(); } - public void Join(Room room, string password) + public void Join(Room room, string password) => Schedule(() => { if (joiningRoomOperation != null) return; @@ -194,25 +194,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge joiningRoomOperation?.Dispose(); joiningRoomOperation = null; }); - } - - private void updateLoadingLayer() - { - if (operationInProgress.Value || !initialRoomsReceived.Value) - loadingLayer.Show(); - else - loadingLayer.Hide(); - } + }); /// /// Push a room as a new subscreen. /// - public virtual void Open(Room room) + public void Open(Room room) => Schedule(() => { // Handles the case where a room is clicked 3 times in quick succession if (!this.IsCurrentScreen()) return; + OpenNewRoom(room); + }); + + protected virtual void OpenNewRoom(Room room) + { selectedRoom.Value = room; this.Push(CreateRoomSubScreen(room)); @@ -221,5 +218,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected abstract FilterControl CreateFilterControl(); protected abstract RoomSubScreen CreateRoomSubScreen(Room room); + + private void updateLoadingLayer() + { + if (operationInProgress.Value || !initialRoomsReceived.Value) + loadingLayer.Show(); + else + loadingLayer.Hide(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 4d20652465..7062994479 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -20,15 +20,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - public override void Open(Room room) + protected override void OpenNewRoom(Room room) { - if (!client.IsConnected.Value) + if (client?.IsConnected.Value != true) { Logger.Log("Not currently connected to the multiplayer server.", LoggingTarget.Runtime, LogLevel.Important); return; } - base.Open(room); + base.OpenNewRoom(room); } } } From 986910a7e4398fb469c643e8feb53526d26d1493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 22 Jul 2021 22:43:35 +0200 Subject: [PATCH 328/489] Annotate dependency as possibly-null --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index e89f3424d9..9f3543d059 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -59,7 +60,7 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerRemoved; [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config, SkinEditorOverlay skinEditor) + private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor) { FlowContent.Spacing = new Vector2(0, 5); From 6dbdfcc70cd9f228707977e34bb56329e97b867f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 22 Jul 2021 23:11:58 +0200 Subject: [PATCH 329/489] Fix room password not being percent-encoded in join request --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index b2d772cac7..d9d4f2eb5c 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,10 +22,10 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; + req.AddParameter(@"password", Password, RequestParameterType.Query); return req; } - // Todo: Password needs to be specified here rather than via AddParameter() because this is a PUT request. May be a framework bug. - protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}?password={Password}"; + protected override string Target => $@"rooms/{Room.RoomID.Value}/users/{User.Id}"; } } From d49d303bae69ce6969932e91ef192a81336c588d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:10:55 +0900 Subject: [PATCH 330/489] Call `GetContainingInputManager` at `LoadComplete` --- .../Edit/Blueprints/JuiceStreamPlacementBlueprint.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 32ab1f19c1..cff5bc2417 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; @@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private int lastEditablePathId = -1; + private InputManager inputManager; + public JuiceStreamPlacementBlueprint() { InternalChildren = new Drawable[] @@ -39,6 +42,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints editablePath.UpdateFrom(HitObjectContainer, HitObject); } + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + protected override bool OnMouseDown(MouseDownEvent e) { switch (PlacementActive) @@ -80,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints break; case PlacementState.Active: - Vector2 unsnappedPosition = GetContainingInputManager().CurrentState.Mouse.Position; + Vector2 unsnappedPosition = inputManager.CurrentState.Mouse.Position; editablePath.MoveLastVertex(unsnappedPosition); break; From 4509c8bcfbcf9fc2a9d6010d707868312fc13cea Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:13:55 +0900 Subject: [PATCH 331/489] Use the more consistent `lastVertex`, with a comment --- .../Blueprints/Components/PlacementEditablePath.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs index 13dfe9db54..fedda20b9d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -9,7 +9,11 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { public class PlacementEditablePath : EditablePath { - private JuiceStreamPathVertex originalNewVertex; + /// + /// The original position of the last added vertex. + /// This is not same as the last vertex of the current path because the vertex ordering can change. + /// + private JuiceStreamPathVertex lastVertex; public PlacementEditablePath(Func positionToDistance) : base(positionToDistance) @@ -27,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components VertexStates[i].VertexBeforeChange = Vertices[i]; } - originalNewVertex = Vertices[index]; + lastVertex = Vertices[index]; } /// @@ -36,8 +40,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void MoveLastVertex(Vector2 screenSpacePosition) { Vector2 position = ToRelativePosition(screenSpacePosition); - double distanceDelta = PositionToDistance(position.Y) - originalNewVertex.Distance; - float xDelta = position.X - originalNewVertex.X; + double distanceDelta = PositionToDistance(position.Y) - lastVertex.Distance; + float xDelta = position.X - lastVertex.X; MoveSelectedVertices(distanceDelta, xDelta); } } From bd3386e770d86da2aee1c2cecf539b30dd6bf30f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:17:42 +0900 Subject: [PATCH 332/489] Fix previously placed vertices in juice stream placement A different UX than not fixing vertices. --- .../Edit/Blueprints/Components/PlacementEditablePath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs index fedda20b9d..158872fbab 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components for (int i = 0; i < VertexCount; i++) { VertexStates[i].IsSelected = i == index; + VertexStates[i].IsFixed = i != index; VertexStates[i].VertexBeforeChange = Vertices[i]; } From b1fd6c0ded7b4a55ecf2489013fcac27ce9d8c3a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:45:33 +0900 Subject: [PATCH 333/489] Apply changes to tests of juice stream placement --- .../TestSceneJuiceStreamPlacementBlueprint.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs index 02861b1adf..cd1fa31b61 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -67,10 +67,19 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor [Test] public void TestVelocityLimit() { - double[] times = { 100, 300, 500 }; - float[] positions = { 200, 300, 100 }; + double[] times = { 100, 300 }; + float[] positions = { 200, 500 }; addPlacementSteps(times, positions); - addPathCheckStep(times, new float[] { 200, 200, 100 }); + addPathCheckStep(times, new float[] { 200, 300 }); + } + + [Test] + public void TestPreviousVerticesAreFixed() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 200, 400, 100, 500 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 200, 300, 200, 300 }); } [Test] From 88d9e2ec06aa450de5bb28fd95807ae40f0fac5c Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 23 Jul 2021 10:23:31 +0800 Subject: [PATCH 334/489] Guard against IndexOutOfRange when parsing launch args --- osu.Desktop/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index cbee1694ba..dc712f2593 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -32,7 +32,7 @@ namespace osu.Desktop var split = arg.Split('='); var key = split[0]; - var val = split[1]; + var val = split.Length > 1 ? split[1] : string.Empty; switch (key) { From ee3791ccf2cea6710d68dab0412f15b620a38f86 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 06:24:58 +0300 Subject: [PATCH 335/489] Update colours once on `TimelineHitObjectBlueprint` --- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index d115c10b5d..6b75696d23 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -123,12 +123,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline indexInCurrentComboBindable.BindValueChanged(_ => updateComboIndex(), true); comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy(); - comboIndexBindable.BindValueChanged(_ => updateColour(), true); - comboIndexWithOffsetsBindable = comboInfo.ComboIndexWithOffsetsBindable.GetBoundCopy(); - comboIndexWithOffsetsBindable.BindValueChanged(_ => updateColour(), true); + comboIndexBindable.ValueChanged += _ => updateColour(); + comboIndexWithOffsetsBindable.ValueChanged += _ => updateColour(); skin.SourceChanged += updateColour; + updateColour(); break; } } From 8600a3bf5b3fc057ce0d342e9c4c1df88501b946 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 07:15:36 +0300 Subject: [PATCH 336/489] Replace "offset" term in combo index documentations with "index" instead --- osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index eddf764ae7..29a56fc625 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -15,21 +15,21 @@ namespace osu.Game.Rulesets.Objects.Types Bindable IndexInCurrentComboBindable { get; } /// - /// The offset of this hitobject in the current combo. + /// The index of this hitobject in the current combo. /// int IndexInCurrentCombo { get; set; } Bindable ComboIndexBindable { get; } /// - /// The offset of this combo in relation to the beatmap. + /// The index of this combo in relation to the beatmap. /// int ComboIndex { get; set; } Bindable ComboIndexWithOffsetsBindable { get; } /// - /// The offset of this combo in relation to the beatmap, with all aggregate s applied. + /// The index of this combo in relation to the beatmap, with all aggregate s applied. /// This should be used instead of only when retrieving combo colours from the beatmap's skin. /// int ComboIndexWithOffsets { get; set; } From 0b3b9e35baa308c5d7778a41c149534e025fc4b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 07:32:56 +0300 Subject: [PATCH 337/489] Also update colours once on `DrawableHitObject` --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 2b222371e2..25f3b8931a 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { base.LoadComplete(); - comboIndexBindable.BindValueChanged(_ => UpdateComboColour(), true); + comboIndexBindable.BindValueChanged(_ => UpdateComboColour()); comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); updateState(ArmedState.Idle, true); From 7bc30b46ff311c3d1fc1d585dc26ddd00c4098e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 07:51:58 +0300 Subject: [PATCH 338/489] Use `BindValueChanged` with last running immediately instead --- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 6b75696d23..6e57b8e88c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -125,10 +125,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy(); comboIndexWithOffsetsBindable = comboInfo.ComboIndexWithOffsetsBindable.GetBoundCopy(); - comboIndexBindable.ValueChanged += _ => updateColour(); - comboIndexWithOffsetsBindable.ValueChanged += _ => updateColour(); + comboIndexBindable.BindValueChanged(_ => updateColour()); + comboIndexWithOffsetsBindable.BindValueChanged(_ => updateColour(), true); + skin.SourceChanged += updateColour; - updateColour(); break; } } From b6c1cf4956f0f28b20735d1c27b8e640714753cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 13:59:51 +0900 Subject: [PATCH 339/489] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 69a89c3cd0..3a9fdfebab 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9825d29405..8ccc3dfbe2 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 3f81b36216..66cae06713 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 17168b8137bc3c68e4f35df75af2f51d2d73cc1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 18:58:22 +0900 Subject: [PATCH 340/489] Fix authentication loss not handled correctly This handles the case where on initial API connection, the server responds with an `Unauthorized` response. It doesn't perform this same checking/handling on every API request, which is probably what we want eventually. Opting to not address the full issue because I know this is going to be a long one (see https://github.com/ppy/osu/blob/05c50c0f6cfafab359963586ec265ad4f143a46c/osu.Game/Online/API/APIAccess.cs#L233). --- osu.Game/Online/API/APIAccess.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 1686595512..eb3abff2b7 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -148,6 +148,16 @@ namespace osu.Game.Online.API var userReq = new GetUserRequest(); + userReq.Failure += ex => + { + if (ex.InnerException is WebException webException && webException.Message == @"Unauthorized") + { + log.Add(@"Login no longer valid"); + Logout(); + } + else + failConnectionProcess(); + }; userReq.Success += u => { localUser.Value = u; @@ -167,6 +177,7 @@ namespace osu.Game.Online.API // getting user's friends is considered part of the connection process. var friendsReq = new GetFriendsRequest(); + friendsReq.Failure += _ => failConnectionProcess(); friendsReq.Success += res => { friends.AddRange(res); From ff3d38de6f49455d7697f91f0ec108665c7f9dcb Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 22:37:08 +0200 Subject: [PATCH 341/489] Localise accuracy display. --- osu.Game/Graphics/UserInterface/PercentageCounter.cs | 3 ++- osu.Game/Graphics/UserInterface/RollingCounter.cs | 5 +++-- osu.Game/Graphics/UserInterface/ScoreCounter.cs | 3 ++- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 4 ++-- .../BeatmapSet/Scores/TopScoreStatisticsSection.cs | 3 ++- osu.Game/Scoring/ScoreInfo.cs | 3 ++- osu.Game/Screens/Play/Break/BreakInfoLine.cs | 5 +++-- osu.Game/Screens/Play/HUD/DefaultComboCounter.cs | 3 ++- .../Ranking/Expanded/Statistics/AccuracyStatistic.cs | 3 ++- osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs | 3 ++- osu.Game/Users/UserStatistics.cs | 3 ++- osu.Game/Utils/FormatUtils.cs | 7 +++---- 12 files changed, 27 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index 2d53ec066b..0ebf2849fe 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osu.Game.Utils; @@ -27,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface Current.Value = DisplayedCount = 1.0f; } - protected override string FormatCount(double count) => count.FormatAccuracy(); + protected override LocalisableString FormatCount(double count) => count.FormatAccuracy(); protected override double GetProportionalDuration(double currentValue, double newValue) { diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index b96181416d..244658b75e 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; namespace osu.Game.Graphics.UserInterface { @@ -137,8 +138,8 @@ namespace osu.Game.Graphics.UserInterface /// Used to format counts. /// /// Count to format. - /// Count formatted as a string. - protected virtual string FormatCount(T count) + /// Count formatted as a localisable string. + protected virtual LocalisableString FormatCount(T count) { return count.ToString(); } diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 5747c846eb..7ebf3819e4 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -3,6 +3,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface @@ -37,7 +38,7 @@ namespace osu.Game.Graphics.UserInterface return currentValue > newValue ? currentValue - newValue : newValue - currentValue; } - protected override string FormatCount(double count) + protected override LocalisableString FormatCount(double count) { string format = new string('0', RequiredDisplayDigits.Value); diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 7108a23e44..934b905a1a 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -372,10 +372,10 @@ namespace osu.Game.Online.Leaderboards public class LeaderboardScoreStatistic { public IconUsage Icon; - public string Value; + public LocalisableString Value; public string Name; - public LeaderboardScoreStatistic(IconUsage icon, string name, string value) + public LeaderboardScoreStatistic(IconUsage icon, string name, LocalisableString value) { Icon = icon; Name = name; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 262f321598..3d5f3f595c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -204,7 +205,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores this.text = text; } - public string Text + public LocalisableString Text { set => text.Text = value; } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a0c4d5a026..890ead40e3 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; @@ -34,7 +35,7 @@ namespace osu.Game.Scoring public double Accuracy { get; set; } [JsonIgnore] - public string DisplayAccuracy => Accuracy.FormatAccuracy(); + public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy(); [JsonProperty(@"pp")] public double? PP { get; set; } diff --git a/osu.Game/Screens/Play/Break/BreakInfoLine.cs b/osu.Game/Screens/Play/Break/BreakInfoLine.cs index 18aab394f8..0bc79d6e77 100644 --- a/osu.Game/Screens/Play/Break/BreakInfoLine.cs +++ b/osu.Game/Screens/Play/Break/BreakInfoLine.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Utils; @@ -63,7 +64,7 @@ namespace osu.Game.Screens.Play.Break valueText.Text = newText; } - protected virtual string Format(T count) + protected virtual LocalisableString Format(T count) { if (count is Enum countEnum) return countEnum.GetDescription(); @@ -86,6 +87,6 @@ namespace osu.Game.Screens.Play.Break { } - protected override string Format(double count) => count.FormatAccuracy(); + protected override LocalisableString Format(double count) => count.FormatAccuracy(); } } diff --git a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs index 718ae24cf1..6d87211ddc 100644 --- a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -31,7 +32,7 @@ namespace osu.Game.Screens.Play.HUD Current.BindTo(scoreProcessor.Combo); } - protected override string FormatCount(int count) + protected override LocalisableString FormatCount(int count) { return $@"{count}x"; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs index 288a107874..476c9fb42f 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -44,7 +45,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING; - protected override string FormatCount(double count) => count.FormatAccuracy(); + protected override LocalisableString FormatCount(double count) => count.FormatAccuracy(); protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => { diff --git a/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs b/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs index 65082d3fae..c54bca9e3a 100644 --- a/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs +++ b/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Ranking.Expanded RelativeSizeAxes = Axes.X; } - protected override string FormatCount(long count) => count.ToString("N0"); + protected override LocalisableString FormatCount(long count) => count.ToString("N0"); protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => { diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 5ddcd86d28..449b0aa212 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -3,6 +3,7 @@ using System; using Newtonsoft.Json; +using osu.Framework.Localisation; using osu.Game.Scoring; using osu.Game.Utils; using static osu.Game.Users.User; @@ -45,7 +46,7 @@ namespace osu.Game.Users public double Accuracy; [JsonIgnore] - public string DisplayAccuracy => (Accuracy / 100).FormatAccuracy(); + public LocalisableString DisplayAccuracy => (Accuracy / 100).FormatAccuracy(); [JsonProperty(@"play_count")] public int PlayCount; diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index df1b6cf00d..e763558647 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.cs @@ -2,8 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Globalization; using Humanizer; +using osu.Framework.Localisation; namespace osu.Game.Utils { @@ -13,9 +13,8 @@ namespace osu.Game.Utils /// Turns the provided accuracy into a percentage with 2 decimal places. /// /// The accuracy to be formatted. - /// An optional format provider. /// formatted accuracy in percentage - public static string FormatAccuracy(this double accuracy, IFormatProvider formatProvider = null) + public static LocalisableString FormatAccuracy(this double accuracy) { // for the sake of display purposes, we don't want to show a user a "rounded up" percentage to the next whole number. // ie. a score which gets 89.99999% shouldn't ever show as 90%. @@ -23,7 +22,7 @@ namespace osu.Game.Utils // percentile with a non-matching grade is confusing. accuracy = Math.Floor(accuracy * 10000) / 10000; - return accuracy.ToString("0.00%", formatProvider ?? CultureInfo.CurrentCulture); + return accuracy.ToLocalisableString("0.00%"); } /// From a3f9d96a8ee1e656a1b07c3318a0a247a1351ee5 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 23:12:22 +0200 Subject: [PATCH 342/489] Localise collapsed header container. --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 5 +++-- .../Profile/Header/Components/OverlinedInfoContainer.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 4195b0b2f1..e29f40f1a5 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -145,8 +146,8 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-"; - hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-"; + hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank.ToLocalisableString("\\##,##0") : "-"; } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs index 8f1bbc4097..5ef8482b47 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Header.Components set => title.Text = value; } - public string Content + public LocalisableString Content { set => content.Text = value; } From 48c21674ed97612bfb71d4fdef38ffb853faba8e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 23:19:51 +0200 Subject: [PATCH 343/489] Localise expanded header container. --- .../Overlays/Profile/Header/DetailHeaderContainer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 6214e504b0..a81396d3b8 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; @@ -172,13 +173,13 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; - ppInfo.Content = user?.Statistics?.PP?.ToString("#,##0") ?? "0"; + ppInfo.Content = user?.Statistics?.PP != null ? (LocalisableString)user?.Statistics?.PP?.ToLocalisableString("#,##0") : "0"; foreach (var scoreRankInfo in scoreRankInfos) scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; - detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-"; - detailCountryRank.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-"; + detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") : "-"; + detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") : "-"; rankGraph.Statistics.Value = user?.Statistics; } @@ -189,7 +190,7 @@ namespace osu.Game.Overlays.Profile.Header public int RankCount { - set => rankCount.Text = value.ToString("#,##0"); + set => rankCount.Text = value.ToLocalisableString("#,##0"); } public ScoreRankInfo(ScoreRank rank) From 011fad167d696b6affac29fa3dce839e03ab1a87 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 23:38:31 +0200 Subject: [PATCH 344/489] Localise rank graph tooltip. --- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 6bf356c0ff..74a25591b4 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -7,6 +7,7 @@ using System.Linq; using Humanizer; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -65,7 +66,7 @@ namespace osu.Game.Overlays.Profile.Header.Components return new TooltipDisplayContent { - Rank = $"#{rank:N0}", + Rank = rank.ToLocalisableString("\\##,##0"), Time = days == 0 ? "now" : $"{"day".ToQuantity(days)} ago" }; } @@ -92,7 +93,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private class TooltipDisplayContent { - public string Rank; + public LocalisableString Rank; public string Time; } } From dce47917fdb5a6b3605ef264cf181acc111c2beb Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 23 Jul 2021 19:04:12 -0700 Subject: [PATCH 345/489] Fix ruleset icons overflowing from settings footer --- osu.Game/Overlays/Settings/SettingsFooter.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index a815480094..ed49ce2b63 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Padding = new MarginPadding { Top = 20, Bottom = 30 }; + Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = SettingsPanel.CONTENT_MARGINS }; var modes = new List(); @@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Settings { var icon = new ConstrainedIconContainer { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Icon = ruleset.CreateInstance().CreateIcon(), Colour = Color4.Gray, Size = new Vector2(20), @@ -47,7 +49,8 @@ namespace osu.Game.Overlays.Settings Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Direction = FillDirection.Full, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Children = modes, Spacing = new Vector2(5), Padding = new MarginPadding { Bottom = 10 }, From d23e47c253ecdd684702d8ee5b0ada0b4f12ec05 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:03:13 +0200 Subject: [PATCH 346/489] Localise level progess bar stats. --- osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index ed89d78a10..877637be22 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateProgress(User user) { levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0; - levelProgressText.Text = user?.Statistics?.Level.Progress.ToString("0'%'"); + levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'"); } } } From c92f69467aebbe47f572fff3a2896c1a21177a19 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:06:31 +0200 Subject: [PATCH 347/489] Localise counter pills. --- osu.Game/Overlays/Profile/Sections/CounterPill.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/CounterPill.cs b/osu.Game/Overlays/Profile/Sections/CounterPill.cs index ca8abcfe5a..34211b40b7 100644 --- a/osu.Game/Overlays/Profile/Sections/CounterPill.cs +++ b/osu.Game/Overlays/Profile/Sections/CounterPill.cs @@ -8,6 +8,7 @@ using osu.Game.Graphics; using osu.Framework.Bindables; using osu.Game.Graphics.Sprites; using osu.Framework.Allocation; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -48,7 +49,7 @@ namespace osu.Game.Overlays.Profile.Sections private void onCurrentChanged(ValueChangedEvent value) { - counter.Text = value.NewValue.ToString("N0"); + counter.Text = value.NewValue.ToLocalisableString("N0"); } } } From 6095aa279176b7e56f2d7bc0d15170e5c579858a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:08:47 +0200 Subject: [PATCH 348/489] Localise profile line chart. --- .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index e6fd09301e..449b1da35d 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -170,7 +170,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Origin = Anchor.CentreRight, RelativePositionAxes = Axes.Y, Margin = new MarginPadding { Right = 3 }, - Text = value.ToString("N0"), + Text = value.ToLocalisableString("N0"), Font = OsuFont.GetFont(size: 12), Y = y }); @@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { Origin = Anchor.CentreLeft, RelativePositionAxes = Axes.X, - Text = value.ToString("MMM yyyy"), + Text = value.ToLocalisableString("MMM yyyy"), Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Rotation = 45, X = x From be26414fe39cc6d05eed2fce4ce26ab66afd1d49 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:13:20 +0200 Subject: [PATCH 349/489] Localise user history graph. --- .../Profile/Sections/Historical/UserHistoryGraph.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index d626c63fed..ac94f0fc87 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -34,8 +34,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical return new TooltipDisplayContent { Name = tooltipCounterName, - Count = playCount.ToString("N0"), - Date = date.ToString("MMMM yyyy") + Count = playCount.ToLocalisableString("N0"), + Date = date.ToLocalisableString("MMMM yyyy") }; } @@ -63,8 +63,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class TooltipDisplayContent { public LocalisableString Name; - public string Count; - public string Date; + public LocalisableString Count; + public LocalisableString Date; } } } From 5b55366178da454fd9f1f007a706150b3874e4af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:14:13 +0200 Subject: [PATCH 350/489] Localise profile header stat buttons. --- .../Profile/Header/Components/ProfileHeaderStatisticsButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index b65d5e2329..1235836aac 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -46,6 +47,6 @@ namespace osu.Game.Overlays.Profile.Header.Components protected abstract IconUsage Icon { get; } - protected void SetValue(int value) => drawableText.Text = value.ToString("#,##0"); + protected void SetValue(int value) => drawableText.Text = value.ToLocalisableString("#,##0"); } } From eba78317d55e4562c46a95205637c973ada48145 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:16:47 +0200 Subject: [PATCH 351/489] Localise kudosu info stats. --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 37de669b3b..eb55a0a78d 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public new int Count { - set => valueText.Text = value.ToString("N0"); + set => valueText.Text = value.ToLocalisableString("N0"); } public CountSection(LocalisableString header) From 48120faeb2f669ab97e6fba4dc9e68f023d9d876 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Jul 2021 19:21:16 +0900 Subject: [PATCH 352/489] Fix inability to join a multiplayer room which has no password --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index d9d4f2eb5c..2a3480c992 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,7 +22,8 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - req.AddParameter(@"password", Password, RequestParameterType.Query); + if (!string.IsNullOrEmpty(Password)) + req.AddParameter(@"password", Password, RequestParameterType.Query); return req; } From e301a996077609f92e1fa6244c4570e96b93ccd0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 12:39:24 +0200 Subject: [PATCH 353/489] Fix accuracy format unit tests. --- osu.Game.Tests/NonVisual/FormatUtilsTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs b/osu.Game.Tests/NonVisual/FormatUtilsTest.cs index df095ddee3..d69822cdc5 100644 --- a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs +++ b/osu.Game.Tests/NonVisual/FormatUtilsTest.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Globalization; using NUnit.Framework; using osu.Game.Utils; @@ -20,7 +19,7 @@ namespace osu.Game.Tests.NonVisual [TestCase(1, "100.00%")] public void TestAccuracyFormatting(double input, string expectedOutput) { - Assert.AreEqual(expectedOutput, input.FormatAccuracy(CultureInfo.InvariantCulture)); + Assert.AreEqual(expectedOutput, input.FormatAccuracy().ToString()); } } } From fa68caa892517f08e9eb4243d7570eb3b3ddceb5 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 19:34:12 +0200 Subject: [PATCH 354/489] Fix CI inspections. --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index e29f40f1a5..f8cf07c69c 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -146,8 +146,8 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank.ToLocalisableString("\\##,##0") : "-"; - hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; } } } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index a81396d3b8..1ddfac3609 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -178,8 +178,8 @@ namespace osu.Game.Overlays.Profile.Header foreach (var scoreRankInfo in scoreRankInfos) scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; - detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") : "-"; - detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") : "-"; + detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; + detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; rankGraph.Statistics.Value = user?.Statistics; } From b0b46eed40ba77d013fb1a20c8e5d84ad9c40fad Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 21:32:26 +0200 Subject: [PATCH 355/489] Apply review suggestions. Co-authored-by: Salman Ahmed --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index f8cf07c69c..f15fa2705a 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -146,8 +146,8 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; - hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; } } } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 1ddfac3609..9e52751904 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -173,13 +173,13 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; - ppInfo.Content = user?.Statistics?.PP != null ? (LocalisableString)user?.Statistics?.PP?.ToLocalisableString("#,##0") : "0"; + ppInfo.Content = user?.Statistics?.PP?.ToLocalisableString("#,##0") ?? (LocalisableString)"0"; foreach (var scoreRankInfo in scoreRankInfos) scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; - detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; - detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; + detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; rankGraph.Statistics.Value = user?.Statistics; } From f6d4ead32ac0a0fcf2a8234ebe1f16e0ca6fc54e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 24 Jul 2021 14:44:22 -0700 Subject: [PATCH 356/489] Fix mod selector overflowing from beatmap info overlay --- .../Overlays/BeatmapSet/LeaderboardModSelector.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 98662e5dea..5b903372fd 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -26,12 +26,14 @@ namespace osu.Game.Overlays.BeatmapSet public LeaderboardModSelector() { - AutoSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; InternalChild = modsContainer = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, Spacing = new Vector2(4), }; @@ -54,7 +56,12 @@ namespace osu.Game.Overlays.BeatmapSet modsContainer.Add(new ModButton(new ModNoMod())); modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.UserPlayable).Select(m => new ModButton(m))); - modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); + modsContainer.ForEach(button => + { + button.Anchor = Anchor.TopCentre; + button.Origin = Anchor.TopCentre; + button.OnSelectionChanged = selectionChanged; + }); } protected override bool OnHover(HoverEvent e) From afaf44d522b51cbc1a84adca3355f3d2605b9eed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Jul 2021 15:07:16 +0900 Subject: [PATCH 357/489] Update `LocalisationAnalyser` and other packages --- osu.Game/osu.Game.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8ccc3dfbe2..4baaf89c67 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,23 +22,23 @@ - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 37cb67d9e09d45be3d623a2e0f709ee3054eae26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Jul 2021 15:24:49 +0900 Subject: [PATCH 358/489] Also update `dotnet-tools` file --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 97fcb52ab1..007e4341b8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -27,10 +27,10 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2021.705.0", + "version": "2021.725.0", "commands": [ "localisation" ] } } -} \ No newline at end of file +} From bb3747ffc91604bc5cab8749d9ac973c02b330c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Jul 2021 17:06:39 +0900 Subject: [PATCH 359/489] Fix beatmap search requests double-escaping Closes #14008. --- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index f1cb02fb10..8ce495e274 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -59,7 +59,7 @@ namespace osu.Game.Online.API.Requests SearchPlayed played = SearchPlayed.Any, SearchExplicit explicitContent = SearchExplicit.Hide) { - this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); + this.query = query; this.ruleset = ruleset; this.cursor = cursor; @@ -78,7 +78,9 @@ namespace osu.Game.Online.API.Requests protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); - req.AddParameter("q", query); + + if (query != null) + req.AddParameter("q", query); if (General != null && General.Any()) req.AddParameter("c", string.Join('.', General.Select(e => e.ToString().ToLowerInvariant()))); From eb585a612039f6d6a8da0b738e5a55f13e427d18 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Sun, 25 Jul 2021 20:40:50 -0400 Subject: [PATCH 360/489] Add "Mirror" mod --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 35 ++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game/Rulesets/Mods/ModMirror.cs | 14 +++++++++ 3 files changed, 50 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs create mode 100644 osu.Game/Rulesets/Mods/ModMirror.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs new file mode 100644 index 0000000000..5eff999343 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModMirror : ModMirror, IApplicableToHitObject + { + public void ApplyToHitObject(HitObject hitObject) + { + var osuObject = (OsuHitObject)hitObject; + + osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); + + if (!(hitObject is Slider slider)) + return; + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + + slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5f37b0d040..27794d6010 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu new OsuModDifficultyAdjust(), new OsuModClassic(), new OsuModRandom(), + new OsuModMirror(), }; case ModType.Automation: diff --git a/osu.Game/Rulesets/Mods/ModMirror.cs b/osu.Game/Rulesets/Mods/ModMirror.cs new file mode 100644 index 0000000000..c4a7738303 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModMirror.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModMirror : Mod + { + public override string Name => "Mirror"; + public override string Acronym => "MR"; + public override ModType Type => ModType.Conversion; + public override string Description => "Flips the beatmap horizontally."; + public override double ScoreMultiplier => 1; + } +} From 2e1cd4a389e8dce86a3f865b99dbed5c0b4b438d Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Sun, 25 Jul 2021 21:26:21 -0400 Subject: [PATCH 361/489] remove accidental tab characters --- osu.Game/Rulesets/Mods/ModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModMirror.cs b/osu.Game/Rulesets/Mods/ModMirror.cs index c4a7738303..4b03d6795d 100644 --- a/osu.Game/Rulesets/Mods/ModMirror.cs +++ b/osu.Game/Rulesets/Mods/ModMirror.cs @@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; public override string Description => "Flips the beatmap horizontally."; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 1; } } From 5141bf66eb56000e9585325c001e00841400348c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 26 Jul 2021 04:28:25 +0300 Subject: [PATCH 362/489] Add failing test case --- .../TestScenePlaylistsLoungeSubScreen.cs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index 7bf161d1d0..79ba6d9660 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -10,6 +10,7 @@ using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Tests.Visual.OnlinePlay; +using osuTK.Input; namespace osu.Game.Tests.Visual.Playlists { @@ -30,17 +31,35 @@ namespace osu.Game.Tests.Visual.Playlists private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First(); + [Test] + public void TestScrollByDraggingRooms() + { + AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left)); + + AddStep("add rooms", () => RoomManager.AddRooms(30)); + + AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0])); + + AddStep("move mouse to third room", () => InputManager.MoveMouseTo(roomsContainer.Rooms[2])); + AddStep("hold down", () => InputManager.PressButton(MouseButton.Left)); + AddStep("drag to top", () => InputManager.MoveMouseTo(roomsContainer.Rooms[0])); + + AddAssert("first and second room masked", () + => !checkRoomVisible(roomsContainer.Rooms[0]) && + !checkRoomVisible(roomsContainer.Rooms[1])); + } + [Test] public void TestScrollSelectedIntoView() { AddStep("add rooms", () => RoomManager.AddRooms(30)); - AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms.First())); + AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0])); - AddStep("select last room", () => roomsContainer.Rooms.Last().Click()); + AddStep("select last room", () => roomsContainer.Rooms[^1].Click()); - AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms.First())); - AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms.Last())); + AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms[0])); + AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1])); } private bool checkRoomVisible(DrawableRoom room) => From 749d7a7b24e329fa68a2a706e721bb729bac9338 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 26 Jul 2021 04:10:49 +0300 Subject: [PATCH 363/489] Fix `DrawableRoom` swallowing mouse down events before reaching its container --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 940ae873ec..adff1cc33e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -275,14 +275,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; - protected override bool OnMouseDown(MouseDownEvent e) - { - if (selectedRoom.Value != Room) - return true; - - return base.OnMouseDown(e); - } - protected override bool OnClick(ClickEvent e) { if (Room != selectedRoom.Value) From 971a67c669e8970c8810f24d17ec88cf26b26494 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 13:31:01 +0900 Subject: [PATCH 364/489] Add failing test coverage for misordered rooms --- .../TestSceneLoungeRoomsContainer.cs | 30 +++++++++++++++++++ .../Visual/OnlinePlay/BasicTestRoomManager.cs | 22 ++++++++++---- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 4d5bf8f225..0a23550f5d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -4,6 +4,8 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; @@ -62,6 +64,31 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("last room selected", () => checkRoomSelected(RoomManager.Rooms.Last())); } + [Test] + public void TestKeyboardNavigationAfterOrderChange() + { + AddStep("add rooms", () => RoomManager.AddRooms(3)); + + AddStep("reorder rooms", () => + { + var room = RoomManager.Rooms[1]; + + RoomManager.RemoveRoom(room); + RoomManager.AddRoom(room); + }); + + AddAssert("no selection", () => checkRoomSelected(null)); + + press(Key.Down); + AddAssert("first room selected", () => checkRoomSelected(getRoomInFlow(0))); + + press(Key.Down); + AddAssert("second room selected", () => checkRoomSelected(getRoomInFlow(1))); + + press(Key.Down); + AddAssert("third room selected", () => checkRoomSelected(getRoomInFlow(2))); + } + [Test] public void TestClickDeselection() { @@ -121,5 +148,8 @@ namespace osu.Game.Tests.Visual.Multiplayer } private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; + + private Room getRoomInFlow(int index) => + (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room; } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index 82c7266598..d37a64fa4b 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -18,11 +18,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// public class BasicTestRoomManager : IRoomManager { - public event Action RoomsUpdated - { - add { } - remove { } - } + public event Action RoomsUpdated; public readonly BindableList Rooms = new BindableList(); @@ -35,8 +31,21 @@ namespace osu.Game.Tests.Visual.OnlinePlay public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1; - Rooms.Add(room); onSuccess?.Invoke(room); + + AddRoom(room); + } + + public void AddRoom(Room room) + { + Rooms.Add(room); + RoomsUpdated?.Invoke(); + } + + public void RemoveRoom(Room room) + { + Rooms.Remove(room); + RoomsUpdated?.Invoke(); } public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) @@ -56,6 +65,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay var room = new Room { RoomID = { Value = i }, + Position = { Value = i }, Name = { Value = $"Room {i}" }, Host = { Value = new User { Username = "Host" } }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, From 3770193edee3bf4ca6449dccc6d4c3b8e8c08b49 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 13:22:11 +0900 Subject: [PATCH 365/489] Fix keyboard navigation at multiplayer lounge not iterating in correct order --- .../Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 07e412ee75..d2253b2d2c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -27,7 +27,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private readonly IBindableList rooms = new BindableList(); private readonly FillFlowContainer roomFlow; - public IReadOnlyList Rooms => roomFlow; + + public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray(); [Resolved(CanBeNull = true)] private Bindable filter { get; set; } From 32e29d042894e36ea86654dc855b8544a3d2721d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 14:29:16 +0900 Subject: [PATCH 366/489] Ensure lounge is loaded before continuing with tests --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 36dd9c2de3..e18fa96218 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -79,6 +79,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); + AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } [Test] From 888954747cfd8dcc9d4561d5d97cad9e57ed78a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 15:47:13 +0900 Subject: [PATCH 367/489] Rename class, add commenting and avoid firing requests to create rooms for testing purposes --- .../Multiplayer/TestSceneMultiplayer.cs | 12 ++++---- .../Navigation/TestSceneScreenNavigation.cs | 4 +-- .../IMultiplayerTestSceneDependencies.cs | 2 +- .../Multiplayer/MultiplayerTestScene.cs | 2 +- .../MultiplayerTestSceneDependencies.cs | 4 +-- .../Multiplayer/TestMultiplayerClient.cs | 4 +-- ...tRequestHandlingMultiplayerRoomManager.cs} | 29 ++++++++++++++----- 7 files changed, 36 insertions(+), 21 deletions(-) rename osu.Game/Tests/Visual/Multiplayer/{TestMultiplayerRoomManager.cs => TestRequestHandlingMultiplayerRoomManager.cs} (85%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index e18fa96218..7c151ffac3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - API.Queue(new CreateRoomRequest(new Room + multiplayerScreen.RoomManager.AddRoom(new Room { Name = { Value = "Test Room" }, Playlist = @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo }, } } - })); + }); }); AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); @@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - API.Queue(new CreateRoomRequest(new Room + multiplayerScreen.RoomManager.AddRoom(new Room { Name = { Value = "Test Room" }, Password = { Value = "password" }, @@ -199,7 +199,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo }, } } - })); + }); }); AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); @@ -433,9 +433,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer { - public new TestMultiplayerRoomManager RoomManager { get; private set; } + public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; } - protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(); + protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager(); } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 52401d32e5..7188a4e57f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -353,10 +353,10 @@ namespace osu.Game.Tests.Visual.Navigation public TestMultiplayer() { - Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager); + Client = new TestMultiplayerClient((TestRequestHandlingMultiplayerRoomManager)RoomManager); } - protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); + protected override RoomManager CreateRoomManager() => new TestRequestHandlingMultiplayerRoomManager(); } } } diff --git a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs index 204c189591..3362ebbbd6 100644 --- a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// The cached . /// - new TestMultiplayerRoomManager RoomManager { get; } + new TestRequestHandlingMultiplayerRoomManager RoomManager { get; } /// /// The cached . diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index b7d3793ab1..42345b7266 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public const int PLAYER_2_ID = 56; public TestMultiplayerClient Client => OnlinePlayDependencies.Client; - public new TestMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager; + public new TestRequestHandlingMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager; public TestUserLookupCache LookupCache => OnlinePlayDependencies?.LookupCache; public TestSpectatorClient SpectatorClient => OnlinePlayDependencies?.SpectatorClient; diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs index a2b0b066a7..2e13fb6a56 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public TestMultiplayerClient Client { get; } public TestUserLookupCache LookupCache { get; } public TestSpectatorClient SpectatorClient { get; } - public new TestMultiplayerRoomManager RoomManager => (TestMultiplayerRoomManager)base.RoomManager; + public new TestRequestHandlingMultiplayerRoomManager RoomManager => (TestRequestHandlingMultiplayerRoomManager)base.RoomManager; public MultiplayerTestSceneDependencies() { @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer CacheAs(SpectatorClient); } - protected override IRoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); + protected override IRoomManager CreateRoomManager() => new TestRequestHandlingMultiplayerRoomManager(); protected virtual TestSpectatorClient CreateSpectatorClient() => new TestSpectatorClient(); } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 1528ed0bc8..3349d670c8 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -38,9 +38,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private BeatmapManager beatmaps { get; set; } = null!; - private readonly TestMultiplayerRoomManager roomManager; + private readonly TestRequestHandlingMultiplayerRoomManager roomManager; - public TestMultiplayerClient(TestMultiplayerRoomManager roomManager) + public TestMultiplayerClient(TestRequestHandlingMultiplayerRoomManager roomManager) { this.roomManager = roomManager; } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs similarity index 85% rename from osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs rename to osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs index 59679f3d66..2e56c8a094 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs @@ -20,7 +20,11 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// A for use in multiplayer test scenes. Should generally not be used by itself outside of a . /// - public class TestMultiplayerRoomManager : MultiplayerRoomManager + /// + /// This implementation will pretend to be a server, handling room retrieval and manipulation requests + /// and returning a roughly expected state, without the need for a server to be running. + /// + public class TestRequestHandlingMultiplayerRoomManager : MultiplayerRoomManager { [Resolved] private IAPIProvider api { get; set; } @@ -33,13 +37,16 @@ namespace osu.Game.Tests.Visual.Multiplayer public new readonly List Rooms = new List(); + private int currentRoomId; + private int currentPlaylistItemId; + [BackgroundDependencyLoader] private void load() { int currentScoreId = 0; - int currentRoomId = 0; - int currentPlaylistItemId = 0; + // Handling here is pretending to be a server, while also updating the local state to match + // how the server would eventually respond and update the RoomManager. ((DummyAPIAccess)api).HandleRequest = req => { switch (req) @@ -48,19 +55,16 @@ namespace osu.Game.Tests.Visual.Multiplayer var apiRoom = new Room(); apiRoom.CopyFrom(createRoomRequest.Room); - apiRoom.RoomID.Value ??= currentRoomId++; // Passwords are explicitly not copied between rooms. apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); apiRoom.Password.Value = createRoomRequest.Room.Password.Value; - for (int i = 0; i < apiRoom.Playlist.Count; i++) - apiRoom.Playlist[i].ID = currentPlaylistItemId++; + AddRoom(apiRoom); var responseRoom = new APICreatedRoom(); responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); - Rooms.Add(apiRoom); createRoomRequest.TriggerSuccess(responseRoom); return true; @@ -128,6 +132,17 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } + public void AddRoom(Room room) + { + room.RoomID.Value ??= currentRoomId++; + for (int i = 0; i < room.Playlist.Count; i++) + room.Playlist[i].ID = currentPlaylistItemId++; + + Rooms.Add(room); + } + + public new void RemoveRoom(Room room) => base.RemoveRoom(room); + private Room createResponseRoom(Room room, bool withParticipants) { var responseRoom = new Room(); From 04c8ea2813659627ad7fac3a7ac05c86533d00cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:33:56 +0900 Subject: [PATCH 368/489] Add failing test for the global ruleset being set to an invalid value --- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 44 +++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index 4e5e8517a4..c52d846a68 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -32,6 +33,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual { [TestFixture] + [HeadlessTest] public class TestSceneOsuGame : OsuTestScene { private IReadOnlyList requiredGameDependencies => new[] @@ -83,10 +85,15 @@ namespace osu.Game.Tests.Visual typeof(PreviewTrackManager), }; + private OsuGame game; + + [Resolved] + private OsuGameBase gameBase { get; set; } + [BackgroundDependencyLoader] - private void load(GameHost host, OsuGameBase gameBase) + private void load(GameHost host) { - OsuGame game = new OsuGame(); + game = new OsuGame(); game.SetHost(host); Children = new Drawable[] @@ -100,7 +107,39 @@ namespace osu.Game.Tests.Visual }; AddUntilStep("wait for load", () => game.IsLoaded); + } + [Test] + public void TestNullRulesetHandled() + { + RulesetInfo ruleset = null; + + AddStep("store current ruleset", () => ruleset = Ruleset.Value); + AddStep("set global ruleset to null value", () => Ruleset.Value = null); + + AddAssert("ruleset still valid", () => Ruleset.Value.Available); + AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset)); + } + + [Test] + public void TestUnavailableRulesetHandled() + { + RulesetInfo ruleset = null; + + AddStep("store current ruleset", () => ruleset = Ruleset.Value); + AddStep("set global ruleset to invalid value", () => Ruleset.Value = new RulesetInfo + { + Name = "unavailable", + Available = false, + }); + + AddAssert("ruleset still valid", () => Ruleset.Value.Available); + AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset)); + } + + [Test] + public void TestAvailableDependencies() + { AddAssert("check OsuGame DI members", () => { foreach (var type in requiredGameDependencies) @@ -111,6 +150,7 @@ namespace osu.Game.Tests.Visual return true; }); + AddAssert("check OsuGameBase DI members", () => { foreach (var type in requiredGameBaseDependencies) From 046f30a268bfa27ed72bd2ebec4b71b87fd1d597 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:34:38 +0900 Subject: [PATCH 369/489] Reject invalid global ruleset values --- osu.Game/OsuGameBase.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4b5fa4f62e..a7fac24351 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -476,13 +476,17 @@ namespace osu.Game private void onRulesetChanged(ValueChangedEvent r) { + if (r.NewValue?.Available != true) + { + // reject the change if the ruleset is not available. + Ruleset.Value = r.OldValue; + return; + } + var dict = new Dictionary>(); - if (r.NewValue?.Available == true) - { - foreach (ModType type in Enum.GetValues(typeof(ModType))) - dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList(); - } + foreach (ModType type in Enum.GetValues(typeof(ModType))) + dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList(); if (!SelectedMods.Disabled) SelectedMods.Value = Array.Empty(); From af9f910a127da8d7763279f1d4494ce2e83f5e4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:59:27 +0900 Subject: [PATCH 370/489] Change `WarningText` to accept `LocalisableString` Can't work just yet, but best to have the flow in place to maintain 100% localisation on classes which were already localised. --- osu.Game/Overlays/Settings/SettingsItem.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index c60ad020f0..feba3ccd46 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -61,12 +61,17 @@ namespace osu.Game.Overlays.Settings /// Text to be displayed at the bottom of this . /// Generally used to recommend the user change their setting as the current one is considered sub-optimal. /// - public string WarningText + public LocalisableString? WarningText { set { + bool hasValue = string.IsNullOrWhiteSpace(value.ToString()); + if (warningText == null) { + if (!hasValue) + return; + // construct lazily for cases where the label is not needed (may be provided by the Control). FlowContent.Add(warningText = new OsuTextFlowContainer { @@ -77,8 +82,8 @@ namespace osu.Game.Overlays.Settings }); } - warningText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; - warningText.Text = value; + warningText.Alpha = hasValue ? 0 : 1; + warningText.Text = value.ToString(); // TODO: Add localisation support after https://github.com/ppy/osu-framework/pull/4603 is merged. } } From b70bd7689e6a2d6202e91070200af1d78c3a3bc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:59:59 +0900 Subject: [PATCH 371/489] Add warning about using high precision mouse on macOS --- osu.Game/Localisation/MouseSettingsStrings.cs | 5 +++++ .../Settings/Sections/Input/MouseSettings.cs | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index 9b1f7fe4c5..fbc48e7d74 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -54,6 +54,11 @@ namespace osu.Game.Localisation /// public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); + /// + /// "This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now." + /// + public static LocalisableString HighPrecisionMacOSWarning => new TranslatableString(getKey(@"high_precision_macos_warning"), @"This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now."); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 753096a207..7047412c9b 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; @@ -28,6 +29,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input private SettingsEnumDropdown confineMouseModeSetting; private Bindable relativeMode; + private SettingsCheckbox highPrecisionMouse; + public MouseSettings(MouseHandler mouseHandler) { this.mouseHandler = mouseHandler; @@ -45,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Children = new Drawable[] { - new SettingsCheckbox + highPrecisionMouse = new SettingsCheckbox { LabelText = MouseSettingsStrings.HighPrecisionMouse, TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip, @@ -107,6 +110,17 @@ namespace osu.Game.Overlays.Settings.Sections.Input confineMouseModeSetting.TooltipText = string.Empty; } }, true); + + highPrecisionMouse.Current.BindValueChanged(highPrecision => + { + if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS) + { + if (highPrecision.NewValue) + highPrecisionMouse.WarningText = MouseSettingsStrings.HighPrecisionMacOSWarning; + else + highPrecisionMouse.WarningText = null; + } + }, true); } private class SensitivitySetting : SettingsSlider From 075507648a946da9ac0acabb7dca749d2b800a52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 17:24:43 +0900 Subject: [PATCH 372/489] Show warning for linux as well --- osu.Game/Localisation/MouseSettingsStrings.cs | 4 ++-- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index fbc48e7d74..1dd4d8e343 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -55,9 +55,9 @@ namespace osu.Game.Localisation public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); /// - /// "This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now." + /// "This setting currently has issues on your platform. It is recommended to adjust sensitivity externally and keep this disabled for now." /// - public static LocalisableString HighPrecisionMacOSWarning => new TranslatableString(getKey(@"high_precision_macos_warning"), @"This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now."); + public static LocalisableString HighPrecisionPlatformWarning => new TranslatableString(getKey(@"high_precision_platform_warning"), @"This setting has known issues on your platform. If you encounter problems, it is recommended to adjust sensitivity externally and keep this disabled for now."); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 7047412c9b..a4da17c5cd 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -113,10 +113,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input highPrecisionMouse.Current.BindValueChanged(highPrecision => { - if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS) + if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows) { if (highPrecision.NewValue) - highPrecisionMouse.WarningText = MouseSettingsStrings.HighPrecisionMacOSWarning; + highPrecisionMouse.WarningText = MouseSettingsStrings.HighPrecisionPlatformWarning; else highPrecisionMouse.WarningText = null; } From c8944b62ec2a9c4a4c89e6761a6902e9f78b7f54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 17:27:39 +0900 Subject: [PATCH 373/489] Update incorrect linked comment --- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index feba3ccd46..bd17c02af9 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Settings } warningText.Alpha = hasValue ? 0 : 1; - warningText.Text = value.ToString(); // TODO: Add localisation support after https://github.com/ppy/osu-framework/pull/4603 is merged. + warningText.Text = value.ToString(); // TODO: Remove ToString() call after TextFlowContainer supports localisation (see https://github.com/ppy/osu-framework/issues/4636). } } From 5984699842064be759742e718a772df232a4ee02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 17:40:07 +0900 Subject: [PATCH 374/489] Update comment to match updated string Co-authored-by: Dan Balasescu --- osu.Game/Localisation/MouseSettingsStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index 1dd4d8e343..5e894c4e0b 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -55,7 +55,7 @@ namespace osu.Game.Localisation public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); /// - /// "This setting currently has issues on your platform. It is recommended to adjust sensitivity externally and keep this disabled for now." + /// "This setting has known issues on your platform. If you encounter problems, it is recommended to adjust sensitivity externally and keep this disabled for now." /// public static LocalisableString HighPrecisionPlatformWarning => new TranslatableString(getKey(@"high_precision_platform_warning"), @"This setting has known issues on your platform. If you encounter problems, it is recommended to adjust sensitivity externally and keep this disabled for now."); From bb046fa3b8694a68e694e1a02aa363a44f3b78aa Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 17:46:56 +0900 Subject: [PATCH 375/489] Move catcher trail generation logic to `Catcher` It resolves mutual dependency of `Catcher` and `CatcherTrailDisplay`. Trail generation logic is moved to `Catcher`. The generation logic no longer uses delayed scheduling because the hidden state is hard to manage. Instead, the last time a trail is generated is calculated and used. The new logic has a different behavior when the dash key is pressed in succession under 50ms, but it is not noticeable for normal plays. --- .../TestSceneCatchSkinConfiguration.cs | 2 +- .../TestSceneCatcher.cs | 12 ++-- .../TestSceneCatcherArea.cs | 6 +- .../TestSceneHyperDashColouring.cs | 12 ++-- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 7 +-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 51 ++++++---------- .../UI/CatcherTrailDisplay.cs | 59 +++++++------------ 7 files changed, 56 insertions(+), 93 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs index 83f28086e6..58ff97d563 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests var skin = new TestSkin { FlipCatcherPlate = flip }; container.Child = new SkinProvidingContainer(skin) { - Child = catcher = new Catcher(new Container(), new DroppedObjectContainer()) + Child = catcher = new Catcher(new CatcherTrailDisplay(), new DroppedObjectContainer()) { Anchor = Anchor.Centre } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index b4282e6784..1e582bd9e8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - private Container trailContainer; + private CatcherTrailDisplay trailDisplay; private DroppedObjectContainer droppedObjectContainer; @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - trailContainer = new Container(); + trailDisplay = new CatcherTrailDisplay(); droppedObjectContainer = new DroppedObjectContainer(); Child = new Container @@ -54,8 +54,8 @@ namespace osu.Game.Rulesets.Catch.Tests Children = new Drawable[] { droppedObjectContainer, - catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty), - trailContainer, + catcher = new TestCatcher(trailDisplay, droppedObjectContainer, difficulty), + trailDisplay, } }; }); @@ -294,8 +294,8 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) - : base(trailsTarget, droppedObjectTarget, difficulty) + public TestCatcher(CatcherTrailDisplay trails, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) + : base(trails, droppedObjectTarget, difficulty) { } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 6a518cf0ef..c97e6bf9ee 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -121,11 +121,13 @@ namespace osu.Game.Rulesets.Catch.Tests { public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) { - var droppedObjectContainer = new DroppedObjectContainer(); + var trailDisplay = new CatcherTrailDisplay { Depth = -1 }; + Add(trailDisplay); + var droppedObjectContainer = new DroppedObjectContainer(); Add(droppedObjectContainer); - Catcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) + Catcher = new Catcher(trailDisplay, droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 73797d0a6a..88ddc7e8a8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -113,30 +113,28 @@ namespace osu.Game.Rulesets.Catch.Tests private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) { - Container trailsContainer = null; - Catcher catcher = null; CatcherTrailDisplay trails = null; + Catcher catcher = null; AddStep("create hyper-dashing catcher", () => { - trailsContainer = new Container(); + trails = new CatcherTrailDisplay(); Child = setupSkinHierarchy(new Container { Anchor = Anchor.Centre, Children = new Drawable[] { - catcher = new Catcher(trailsContainer, new DroppedObjectContainer()) + catcher = new Catcher(trails, new DroppedObjectContainer()) { Scale = new Vector2(4) }, - trailsContainer + trails } }, skin); }); - AddStep("get trails container", () => + AddStep("start hyper-dash", () => { - trails = trailsContainer.OfType().Single(); catcher.SetHyperDashState(2); }); diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index b43815a8bd..bcbe7c776e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -45,14 +44,14 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - var trailContainer = new Container + var trailDisplay = new CatcherTrailDisplay { Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft }; var droppedObjectContainer = new DroppedObjectContainer(); - Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty) + Catcher = new Catcher(trailDisplay, droppedObjectContainer, difficulty) { X = CENTER_X }; @@ -70,7 +69,7 @@ namespace osu.Game.Rulesets.Catch.UI Origin = Anchor.TopLeft, Catcher = Catcher, }, - trailContainer, + trailDisplay, HitObjectContainer, }); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 49508b1caf..f8b0dabeb9 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -71,10 +71,10 @@ namespace osu.Game.Rulesets.Catch.UI /// private const float caught_fruit_scale_adjust = 0.5f; - [NotNull] - private readonly Container trailsTarget; - - private CatcherTrailDisplay trails; + /// + /// Contains trails and afterimages (also called "end glow" in code) of the catcher. + /// + private readonly CatcherTrailDisplay trails; /// /// Contains caught objects on the plate. @@ -92,20 +92,7 @@ namespace osu.Game.Rulesets.Catch.UI private set => Body.AnimationState.Value = value; } - private bool dashing; - - public bool Dashing - { - get => dashing; - set - { - if (value == dashing) return; - - dashing = value; - - updateTrailVisibility(); - } - } + public bool Dashing { get; set; } /// /// The currently facing direction. @@ -138,9 +125,9 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] CatcherTrailDisplay trails, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { - this.trailsTarget = trailsTarget; + this.trails = trails; this.droppedObjectTarget = droppedObjectTarget; Origin = Anchor.TopCentre; @@ -177,15 +164,6 @@ namespace osu.Game.Rulesets.Catch.UI private void load(OsuConfigManager config) { hitLighting = config.GetBindable(OsuSetting.HitLighting); - trails = new CatcherTrailDisplay(this); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // don't add in above load as we may potentially modify a parent in an unsafe manner. - trailsTarget.Add(trails); } /// @@ -313,7 +291,7 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { - trails.DisplayEndGlow(); + trails.DisplayEndGlow(CurrentState, X, Scale * Body.Scale); runHyperDashStateTransition(true); } } @@ -331,13 +309,9 @@ namespace osu.Game.Rulesets.Catch.UI private void runHyperDashStateTransition(bool hyperDashing) { - updateTrailVisibility(); - this.FadeColour(hyperDashing ? hyperDashColour : Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); } - private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing; - protected override void SkinChanged(ISkinSource skin) { base.SkinChanged(skin); @@ -373,6 +347,15 @@ namespace osu.Game.Rulesets.Catch.UI X = hyperDashTargetPosition; SetHyperDashState(); } + + if (Dashing || HyperDashing) + { + double lastTrailTime = trails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; + double generationInterval = HyperDashing ? 25 : 50; + + if (Time.Current - lastTrailTime >= generationInterval) + trails.DisplayDashTrail(CurrentState, X, Scale * Body.Scale, HyperDashing); + } } private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index b59fabcb70..347df5f114 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -1,7 +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 System.Linq; using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,7 +17,10 @@ namespace osu.Game.Rulesets.Catch.UI /// public class CatcherTrailDisplay : CompositeDrawable { - private readonly Catcher catcher; + [CanBeNull] + public CatcherTrail LastDashTrail => dashTrails.Concat(hyperDashTrails) + .OrderByDescending(trail => trail.LifetimeStart) + .FirstOrDefault(); private readonly DrawablePool trailPool; @@ -55,30 +58,8 @@ namespace osu.Game.Rulesets.Catch.UI } } - private bool trail; - - /// - /// Whether to start displaying trails following the catcher. - /// - public bool DisplayTrail + public CatcherTrailDisplay() { - get => trail; - set - { - if (trail == value) - return; - - trail = value; - - if (trail) - displayTrail(); - } - } - - public CatcherTrailDisplay([NotNull] Catcher catcher) - { - this.catcher = catcher ?? throw new ArgumentNullException(nameof(catcher)); - RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -93,9 +74,11 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Displays a single end-glow catcher sprite. /// - public void DisplayEndGlow() + public void DisplayEndGlow(CatcherAnimationState animationState, float x, Vector2 scale) { - var endGlow = createTrailSprite(endGlowSprites); + var endGlow = createTrail(animationState, x, scale); + + endGlowSprites.Add(endGlow); endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In); @@ -103,28 +86,26 @@ namespace osu.Game.Rulesets.Catch.UI endGlow.Expire(true); } - private void displayTrail() + public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) { - if (!DisplayTrail) - return; + var sprite = createTrail(animationState, x, scale); - var sprite = createTrailSprite(catcher.HyperDashing ? hyperDashTrails : dashTrails); + if (hyperDashing) + hyperDashTrails.Add(sprite); + else + dashTrails.Add(sprite); sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); sprite.Expire(true); - - Scheduler.AddDelayed(displayTrail, catcher.HyperDashing ? 25 : 50); } - private CatcherTrail createTrailSprite(Container target) + private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale) { CatcherTrail sprite = trailPool.Get(); - sprite.AnimationState = catcher.CurrentState; - sprite.Scale = catcher.Scale * catcher.Body.Scale; - sprite.Position = catcher.Position; - - target.Add(sprite); + sprite.AnimationState = animationState; + sprite.Scale = scale; + sprite.Position = new Vector2(x, 0); return sprite; } From c08130398cd604aa6a2235879ffc2601e8aa5c8e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 17:50:10 +0900 Subject: [PATCH 376/489] Add some comments --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +++ osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index f8b0dabeb9..fa444fdad7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -92,6 +92,9 @@ namespace osu.Game.Rulesets.Catch.UI private set => Body.AnimationState.Value = value; } + /// + /// Whether the catcher is currently dashing. + /// public bool Dashing { get; set; } /// diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 347df5f114..00f2a76bde 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -17,6 +17,10 @@ namespace osu.Game.Rulesets.Catch.UI /// public class CatcherTrailDisplay : CompositeDrawable { + /// + /// The most recent dash trail added in this container. + /// Only alive (not faded out) trails are considered. + /// [CanBeNull] public CatcherTrail LastDashTrail => dashTrails.Concat(hyperDashTrails) .OrderByDescending(trail => trail.LifetimeStart) From 428244227818b5267021734ab907074e49c919cb Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 17:50:52 +0900 Subject: [PATCH 377/489] Make `Catcher.body` private as it is no longer needed by `CatcherTrailDisplay` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index fa444fdad7..e7c26ee44f 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -88,8 +88,8 @@ namespace osu.Game.Rulesets.Catch.UI public CatcherAnimationState CurrentState { - get => Body.AnimationState.Value; - private set => Body.AnimationState.Value = value; + get => body.AnimationState.Value; + private set => body.AnimationState.Value = value; } /// @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Catch.UI /// private readonly float catchWidth; - internal readonly SkinnableCatcher Body; + private readonly SkinnableCatcher body; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.UI // offset fruit vertically to better place "above" the plate. Y = -5 }, - Body = new SkinnableCatcher(), + body = new SkinnableCatcher(), hitExplosionContainer = new HitExplosionContainer { Anchor = Anchor.TopCentre, @@ -294,7 +294,7 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { - trails.DisplayEndGlow(CurrentState, X, Scale * Body.Scale); + trails.DisplayEndGlow(CurrentState, X, Scale * body.Scale); runHyperDashStateTransition(true); } } @@ -340,7 +340,7 @@ namespace osu.Game.Rulesets.Catch.UI base.Update(); var scaleFromDirection = new Vector2((int)VisualDirection, 1); - Body.Scale = scaleFromDirection; + body.Scale = scaleFromDirection; caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One; // Correct overshooting. @@ -357,7 +357,7 @@ namespace osu.Game.Rulesets.Catch.UI double generationInterval = HyperDashing ? 25 : 50; if (Time.Current - lastTrailTime >= generationInterval) - trails.DisplayDashTrail(CurrentState, X, Scale * Body.Scale, HyperDashing); + trails.DisplayDashTrail(CurrentState, X, Scale * body.Scale, HyperDashing); } } From 0fbe950a3c452f645f811147d624836c4ca9c996 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Jun 2021 16:26:39 +0900 Subject: [PATCH 378/489] Modify catcher autoplay test pattern to see more variety movement --- osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index f552c3c27b..0ca0ddfcb1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Catch.Tests } }; - for (int i = 0; i < 100; i++) + for (int i = 0; i < 12; i++) { - float width = (i % 10 + 1) / 20f * CatchPlayfield.WIDTH; + float width = (i + 1) / 20f * CatchPlayfield.WIDTH; beatmap.HitObjects.Add(new JuiceStream { @@ -39,8 +39,8 @@ namespace osu.Game.Rulesets.Catch.Tests Vector2.Zero, new Vector2(width, 0) }), - StartTime = i * 2000, - NewCombo = i % 8 == 0, + StartTime = i * 1000, + NewCombo = i % 5 == 0, Samples = new List(new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100) From 8045534fa511d93b9e9be5c5b98ff2e2369c9bcb Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 18:18:24 +0900 Subject: [PATCH 379/489] Remove outdated comment and simplify code --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 49508b1caf..b724b84a56 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -218,14 +218,9 @@ namespace osu.Game.Rulesets.Catch.UI if (!(hitObject is PalpableCatchHitObject fruit)) return false; - var halfCatchWidth = catchWidth * 0.5f; - - // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. - var catchObjectPosition = fruit.EffectiveX; - var catcherPosition = Position.X; - - return catchObjectPosition >= catcherPosition - halfCatchWidth && - catchObjectPosition <= catcherPosition + halfCatchWidth; + float halfCatchWidth = catchWidth * 0.5f; + return fruit.EffectiveX >= X - halfCatchWidth && + fruit.EffectiveX <= X + halfCatchWidth; } public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result) From 55e8a44db605f24af80cc71c53c372b5289bc989 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 18:15:59 +0700 Subject: [PATCH 380/489] add test for DrawableComment Can reproduce the issue at https://github.com/ppy/osu/issues/13993 --- .../Visual/Online/TestSceneDrawableComment.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs new file mode 100644 index 0000000000..26580d02d8 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -0,0 +1,75 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.Comments; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneDrawableComment : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private Container container; + + [SetUp] + public void SetUp() => Schedule(() => + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + }, + container = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + }; + }); + + [TestCaseSource(nameof(comments))] + public void TestComment(string description, string text) + { + AddStep(description, () => + { + comment.Message = text; + container.Add(new DrawableComment(comment)); + }); + } + + private static readonly Comment comment = new Comment + { + Id = 1, + LegacyName = "Test User", + CreatedAt = DateTimeOffset.Now, + VotesCount = 0, + }; + + private static object[] comments = + { + new[] { "Plain", "This is plain comment" }, + // Taken from https://github.com/ppy/osu/issues/13993#issuecomment-885994077 + new[] + { + "Problematic", @"My tablet doesn't work :( +It's a Huion 420 and it's apparently incompatible with OpenTablet Driver. The warning I get is: ""DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"" and it repeats 4 times on the notification before logging subsequent warnings. +Checking the logs, it looks for other Huion tablets before sending the notification (e.g. + ""2021-07-23 03:52:33 [verbose]: Detect: Searching for tablet 'Huion WH1409 V2' + 20 2021-07-23 03:52:33 [error]: DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"") +I use an Arch based installation of Linux and the tablet runs perfectly with Digimend kernel driver, with area configuration, pen pressure, etc. On osu!lazer the cursor disappears until I set it to ""Borderless"" instead of ""Fullscreen"" and even after it shows up, it goes to the bottom left corner as soon as a map starts. +I have honestly 0 idea of whats going on at this point." + } + }; + } +} From 43100c5288099d7c7eadb5306d61d23080cf2008 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 18:18:33 +0700 Subject: [PATCH 381/489] initial CommentMarkdownContainer --- .../Comments/CommentMarkdownContainer.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 osu.Game/Overlays/Comments/CommentMarkdownContainer.cs diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs new file mode 100644 index 0000000000..0b0d4d9a58 --- /dev/null +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Markdig.Syntax.Inlines; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Game.Graphics.Containers.Markdown; + +namespace osu.Game.Overlays.Comments +{ + public class CommentMarkdownContainer : OsuMarkdownContainer + { + public override MarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer(); + + private class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer + { + // Don't render image in comment for now + protected override void AddImage(LinkInline linkInline) { } + } + } +} From 2a6aeb5310809d58bcf969958ab105d47a749265 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 18:18:55 +0700 Subject: [PATCH 382/489] use CommentMarkdownContainer in DrawableContainer --- osu.Game/Overlays/Comments/DrawableComment.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index d94f8c4b8b..3520b15b1e 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Bindables; using System.Linq; using osu.Game.Graphics.Sprites; -using osu.Game.Online.Chat; using osu.Framework.Allocation; using System.Collections.Generic; using System; @@ -61,7 +60,7 @@ namespace osu.Game.Overlays.Comments { LinkFlowContainer username; FillFlowContainer info; - LinkFlowContainer message; + CommentMarkdownContainer message; GridContainer content; VotePill votePill; @@ -153,10 +152,12 @@ namespace osu.Game.Overlays.Comments } } }, - message = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14)) + message = new CommentMarkdownContainer { RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y + AutoSizeAxes = Axes.Y, + DocumentMargin = new MarginPadding(0), + DocumentPadding = new MarginPadding(0), }, info = new FillFlowContainer { @@ -275,10 +276,7 @@ namespace osu.Game.Overlays.Comments } if (Comment.HasMessage) - { - var formattedSource = MessageFormatter.FormatText(Comment.Message); - message.AddLinks(formattedSource.Text, formattedSource.Links); - } + message.Text = Comment.Message; if (Comment.IsDeleted) { From f80c46e2a01d94e6dd5939da4ed7eab5e1863d80 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 21:02:57 +0700 Subject: [PATCH 383/489] add heading test --- .../Visual/Online/TestSceneDrawableComment.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs index 26580d02d8..b93e7a1739 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -59,6 +59,17 @@ namespace osu.Game.Tests.Visual.Online private static object[] comments = { new[] { "Plain", "This is plain comment" }, + + new[] + { + "Heading", @"# Heading 1 +## Heading 2 +### Heading 3 +#### Heading 4 +##### Heading 5 +###### Heading 6" + }, + // Taken from https://github.com/ppy/osu/issues/13993#issuecomment-885994077 new[] { From 6631f0de1958755724c8f5c375b544e79def2ec6 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 21:07:35 +0700 Subject: [PATCH 384/489] add CommentMarkdownHeading --- .../Comments/CommentMarkdownContainer.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index 0b0d4d9a58..aeab292b0d 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.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 Markdig.Syntax; using Markdig.Syntax.Inlines; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; @@ -11,10 +12,37 @@ namespace osu.Game.Overlays.Comments { public override MarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer(); + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock); + private class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { // Don't render image in comment for now protected override void AddImage(LinkInline linkInline) { } } + + private class CommentMarkdownHeading : OsuMarkdownHeading + { + public CommentMarkdownHeading(HeadingBlock headingBlock) + : base(headingBlock) + { + } + + protected override float GetFontSizeByLevel(int level) + { + var defaultFontSize = base.GetFontSizeByLevel(6); + + switch (level) + { + case 1: + return 1.2f * defaultFontSize; + + case 2: + return 1.1f * defaultFontSize; + + default: + return defaultFontSize; + } + } + } } } From dc864abbd896662ee4d2c7f4f6aeff1e08cd500f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 21:08:57 +0700 Subject: [PATCH 385/489] add link test --- osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs index b93e7a1739..7b741accbb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -59,6 +59,7 @@ namespace osu.Game.Tests.Visual.Online private static object[] comments = { new[] { "Plain", "This is plain comment" }, + new[] { "Link", "Please visit https://osu.ppy.sh" }, new[] { From 49160e4482a0cf54f1ccd783b773b1a35ad1d1da Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 26 Jul 2021 10:46:41 -0400 Subject: [PATCH 386/489] review modifications: maniamodmirror inheritance, reflection utilities, vertical flip option --- .../Mods/ManiaModMirror.cs | 6 +-- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 15 +----- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 30 ++++++----- .../Utils/OsuHitObjectGenerationUtils.cs | 52 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModMirror.cs | 1 - 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index cf404cc98e..f01884c97f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -10,13 +10,9 @@ using osu.Game.Rulesets.Mania.Beatmaps; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModMirror : Mod, IApplicableToBeatmap + public class ManiaModMirror : ModMirror, IApplicableToBeatmap { - public override string Name => "Mirror"; - public override string Acronym => "MR"; - public override ModType Type => ModType.Conversion; public override string Description => "Notes are flipped horizontally."; - public override double ScoreMultiplier => 1; public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 16c166257a..a693e50016 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Osu.Utils; using osuTK; namespace osu.Game.Rulesets.Osu.Mods @@ -19,19 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var osuObject = (OsuHitObject)hitObject; - osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); - - if (!(hitObject is Slider slider)) - return; - - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - - var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); - - slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index 5eff999343..a8cf72a5c7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -2,34 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Framework.Bindables; +using osu.Game.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Osu.Utils; using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModMirror : ModMirror, IApplicableToHitObject { + public override string Description => "Reflect the playfield."; + + [SettingSource("Reflect Horizontally", "Reflect the playfield horizontally.")] + public Bindable ReflectY { get; } = new BindableBool(true); + [SettingSource("Reflect Vertically", "Reflect the playfield vertically.")] + public Bindable ReflectX { get; } = new BindableBool(false); + public void ApplyToHitObject(HitObject hitObject) { + if (!(ReflectY.Value || ReflectX.Value)) + return; // TODO deselect the mod if possible so replays and submissions don't have purposeless mods attached. var osuObject = (OsuHitObject)hitObject; - - osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); - - if (!(hitObject is Slider slider)) - return; - - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); - - var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - - slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + if (ReflectY.Value) + OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + if (ReflectX.Value) + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); } } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index 06b964a647..f4ab60367e 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -2,7 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.Utils @@ -100,5 +104,53 @@ namespace osu.Game.Rulesets.Osu.Utils initial.Length * MathF.Sin(finalAngleRad) ); } + + /// + /// Reflects an OsuHitObject's position horizontally (over the 'y axis'). + /// + /// The OsuHitObject to be reflected. + /// The reflected OsuHitObject. + public static OsuHitObject ReflectOsuHitObjectHorizontally(OsuHitObject osuObject) + { + osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); + + if (!(osuObject is Slider slider)) + return osuObject; + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + + slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + + return osuObject; + } + + /// + /// Reflects an OsuHitObject's position vertically (over the 'x axis'). + /// + /// The OsuHitObject to be reflected. + /// The reflected OsuHitObject. + public static OsuHitObject ReflectOsuHitObjectVertically(OsuHitObject osuObject) + { + osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); + + if (!(osuObject is Slider slider)) + return osuObject; + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); + + slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + + return osuObject; + } } } diff --git a/osu.Game/Rulesets/Mods/ModMirror.cs b/osu.Game/Rulesets/Mods/ModMirror.cs index 4b03d6795d..3c4b7d0c60 100644 --- a/osu.Game/Rulesets/Mods/ModMirror.cs +++ b/osu.Game/Rulesets/Mods/ModMirror.cs @@ -8,7 +8,6 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; - public override string Description => "Flips the beatmap horizontally."; public override double ScoreMultiplier => 1; } } From c7c261ba037f02449d5f7dcabf9b1837839fa581 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 26 Jul 2021 17:48:03 -0400 Subject: [PATCH 387/489] review modifications: change xmldoc wording, configure with enum instead of bool, declare incompatibility with hr --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 3 ++ osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 34 +++++++++++++------ .../Utils/OsuHitObjectGenerationUtils.cs | 4 +-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index a693e50016..82b8959456 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.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 System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; @@ -16,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Mods { public override double ScoreMultiplier => 1.06; + public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust), typeof(ModMirror) }; + public void ApplyToHitObject(HitObject hitObject) { var osuObject = (OsuHitObject)hitObject; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index a8cf72a5c7..fd1d685fd1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.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 System.Linq; using osu.Framework.Bindables; using osu.Game.Configuration; @@ -17,21 +18,34 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModMirror : ModMirror, IApplicableToHitObject { public override string Description => "Reflect the playfield."; + public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - [SettingSource("Reflect Horizontally", "Reflect the playfield horizontally.")] - public Bindable ReflectY { get; } = new BindableBool(true); - [SettingSource("Reflect Vertically", "Reflect the playfield vertically.")] - public Bindable ReflectX { get; } = new BindableBool(false); + [SettingSource("Reflection", "Change the type of reflection.")] + public Bindable Reflection { get; } = new Bindable(); public void ApplyToHitObject(HitObject hitObject) { - if (!(ReflectY.Value || ReflectX.Value)) - return; // TODO deselect the mod if possible so replays and submissions don't have purposeless mods attached. var osuObject = (OsuHitObject)hitObject; - if (ReflectY.Value) - OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); - if (ReflectX.Value) - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + switch (Reflection.Value) + { + case MirrorType.Horizontal: + OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + break; + case MirrorType.Vertical: + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + break; + case MirrorType.Both: + OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + break; + } + } + + public enum MirrorType + { + Horizontal, + Vertical, + Both } } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index f4ab60367e..bf55898901 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects an OsuHitObject's position horizontally (over the 'y axis'). + /// Reflects an OsuHitObject's position horizontally. /// /// The OsuHitObject to be reflected. /// The reflected OsuHitObject. @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects an OsuHitObject's position vertically (over the 'x axis'). + /// Reflects an OsuHitObject's position vertically. /// /// The OsuHitObject to be reflected. /// The reflected OsuHitObject. From d9d9db6f621ea2353e11906bf334a1e9d98d962c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 19:00:08 +0900 Subject: [PATCH 388/489] Revert "Modify catcher autoplay test pattern to see more variety movement" I found `TestSceneHyperDash` is useful for visual inspection of hyper dash trails. This reverts commit 0fbe950a --- osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 0ca0ddfcb1..f552c3c27b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Catch.Tests } }; - for (int i = 0; i < 12; i++) + for (int i = 0; i < 100; i++) { - float width = (i + 1) / 20f * CatchPlayfield.WIDTH; + float width = (i % 10 + 1) / 20f * CatchPlayfield.WIDTH; beatmap.HitObjects.Add(new JuiceStream { @@ -39,8 +39,8 @@ namespace osu.Game.Rulesets.Catch.Tests Vector2.Zero, new Vector2(width, 0) }), - StartTime = i * 1000, - NewCombo = i % 5 == 0, + StartTime = i * 2000, + NewCombo = i % 8 == 0, Samples = new List(new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100) From de68fd12b3ef0f0e9a6df12a6fc05ff3eb0a53ce Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 18:48:31 +0900 Subject: [PATCH 389/489] Move catcher trail colouring logic to `CatcherTrailDisplay` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 8 ---- .../UI/CatcherTrailDisplay.cs | 46 ++++++------------- 2 files changed, 15 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index e7c26ee44f..a5f51374c4 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -115,7 +115,6 @@ namespace osu.Game.Rulesets.Catch.UI private readonly SkinnableCatcher body; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; - private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; private double hyperDashModifier = 1; private int hyperDashDirection; @@ -323,13 +322,6 @@ namespace osu.Game.Rulesets.Catch.UI skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? DEFAULT_HYPER_DASH_COLOUR; - hyperDashEndGlowColour = - skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? - hyperDashColour; - - trails.HyperDashTrailsColour = hyperDashColour; - trails.EndGlowSpritesColour = hyperDashEndGlowColour; - flipCatcherPlate = skin.GetConfig(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true; runHyperDashStateTransition(HyperDashing); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 00f2a76bde..9931ef8f64 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -6,6 +6,8 @@ using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -15,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Represents a component responsible for displaying /// the appropriate catcher trails when requested to. /// - public class CatcherTrailDisplay : CompositeDrawable + public class CatcherTrailDisplay : SkinReloadableDrawable { /// /// The most recent dash trail added in this container. @@ -26,42 +28,16 @@ namespace osu.Game.Rulesets.Catch.UI .OrderByDescending(trail => trail.LifetimeStart) .FirstOrDefault(); + public Color4 HyperDashTrailsColour => hyperDashTrails.Colour; + + public Color4 EndGlowSpritesColour => endGlowSprites.Colour; + private readonly DrawablePool trailPool; private readonly Container dashTrails; private readonly Container hyperDashTrails; private readonly Container endGlowSprites; - private Color4 hyperDashTrailsColour = Catcher.DEFAULT_HYPER_DASH_COLOUR; - - public Color4 HyperDashTrailsColour - { - get => hyperDashTrailsColour; - set - { - if (hyperDashTrailsColour == value) - return; - - hyperDashTrailsColour = value; - hyperDashTrails.Colour = hyperDashTrailsColour; - } - } - - private Color4 endGlowSpritesColour = Catcher.DEFAULT_HYPER_DASH_COLOUR; - - public Color4 EndGlowSpritesColour - { - get => endGlowSpritesColour; - set - { - if (endGlowSpritesColour == value) - return; - - endGlowSpritesColour = value; - endGlowSprites.Colour = endGlowSpritesColour; - } - } - public CatcherTrailDisplay() { RelativeSizeAxes = Axes.Both; @@ -75,6 +51,14 @@ namespace osu.Game.Rulesets.Catch.UI }; } + protected override void SkinChanged(ISkinSource skin) + { + base.SkinChanged(skin); + + hyperDashTrails.Colour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; + endGlowSprites.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; + } + /// /// Displays a single end-glow catcher sprite. /// From da69867fd4b452bc2765b43006ba3bbedc86cf6b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 18:59:55 +0900 Subject: [PATCH 390/489] Move catcher trail generation logic to `CatcherArea` --- .../TestSceneCatchSkinConfiguration.cs | 2 +- .../TestSceneCatcher.cs | 10 ++-- .../TestSceneCatcherArea.cs | 5 +- .../TestSceneHyperDashColouring.cs | 10 ++-- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 8 +--- osu.Game.Rulesets.Catch/UI/Catcher.cs | 22 ++------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 48 +++++++++++++------ 7 files changed, 48 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs index 58ff97d563..8ae2bcca0e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests var skin = new TestSkin { FlipCatcherPlate = flip }; container.Child = new SkinProvidingContainer(skin) { - Child = catcher = new Catcher(new CatcherTrailDisplay(), new DroppedObjectContainer()) + Child = catcher = new Catcher(new DroppedObjectContainer()) { Anchor = Anchor.Centre } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 1e582bd9e8..540f02580f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - private CatcherTrailDisplay trailDisplay; - private DroppedObjectContainer droppedObjectContainer; private TestCatcher catcher; @@ -45,7 +43,6 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - trailDisplay = new CatcherTrailDisplay(); droppedObjectContainer = new DroppedObjectContainer(); Child = new Container @@ -54,8 +51,7 @@ namespace osu.Game.Rulesets.Catch.Tests Children = new Drawable[] { droppedObjectContainer, - catcher = new TestCatcher(trailDisplay, droppedObjectContainer, difficulty), - trailDisplay, + catcher = new TestCatcher(droppedObjectContainer, difficulty), } }; }); @@ -294,8 +290,8 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(CatcherTrailDisplay trails, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) - : base(trails, droppedObjectTarget, difficulty) + public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) + : base(droppedObjectTarget, difficulty) { } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index c97e6bf9ee..a3307c9224 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -121,13 +121,10 @@ namespace osu.Game.Rulesets.Catch.Tests { public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) { - var trailDisplay = new CatcherTrailDisplay { Depth = -1 }; - Add(trailDisplay); - var droppedObjectContainer = new DroppedObjectContainer(); Add(droppedObjectContainer); - Catcher = new Catcher(trailDisplay, droppedObjectContainer, beatmapDifficulty) + Catcher = new Catcher(droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 88ddc7e8a8..4524086b02 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -118,19 +118,19 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("create hyper-dashing catcher", () => { - trails = new CatcherTrailDisplay(); + CatcherArea catcherArea; Child = setupSkinHierarchy(new Container { Anchor = Anchor.Centre, - Children = new Drawable[] + Child = catcherArea = new CatcherArea { - catcher = new Catcher(trails, new DroppedObjectContainer()) + Catcher = catcher = new Catcher(new DroppedObjectContainer()) { Scale = new Vector2(4) - }, - trails + } } }, skin); + trails = catcherArea.CatcherTrails; }); AddStep("start hyper-dash", () => diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index bcbe7c776e..1e20643a08 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -44,14 +44,9 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - var trailDisplay = new CatcherTrailDisplay - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft - }; var droppedObjectContainer = new DroppedObjectContainer(); - Catcher = new Catcher(trailDisplay, droppedObjectContainer, difficulty) + Catcher = new Catcher(droppedObjectContainer, difficulty) { X = CENTER_X }; @@ -69,7 +64,6 @@ namespace osu.Game.Rulesets.Catch.UI Origin = Anchor.TopLeft, Catcher = Catcher, }, - trailDisplay, HitObjectContainer, }); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a5f51374c4..dd6d8626d0 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -71,11 +71,6 @@ namespace osu.Game.Rulesets.Catch.UI /// private const float caught_fruit_scale_adjust = 0.5f; - /// - /// Contains trails and afterimages (also called "end glow" in code) of the catcher. - /// - private readonly CatcherTrailDisplay trails; - /// /// Contains caught objects on the plate. /// @@ -102,6 +97,8 @@ namespace osu.Game.Rulesets.Catch.UI /// public Direction VisualDirection { get; set; } = Direction.Right; + public Vector2 BodyScale => Scale * body.Scale; + /// /// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed. /// @@ -127,9 +124,8 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] CatcherTrailDisplay trails, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { - this.trails = trails; this.droppedObjectTarget = droppedObjectTarget; Origin = Anchor.TopCentre; @@ -292,10 +288,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashTargetPosition = targetPosition; if (!wasHyperDashing) - { - trails.DisplayEndGlow(CurrentState, X, Scale * body.Scale); runHyperDashStateTransition(true); - } } } @@ -342,15 +335,6 @@ namespace osu.Game.Rulesets.Catch.UI X = hyperDashTargetPosition; SetHyperDashState(); } - - if (Dashing || HyperDashing) - { - double lastTrailTime = trails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; - double generationInterval = HyperDashing ? 25 : 50; - - if (Time.Current - lastTrailTime >= generationInterval) - trails.DisplayDashTrail(CurrentState, X, Scale * body.Scale, HyperDashing); - } } private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index de0ace9817..bcf4b36c9c 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -25,15 +25,13 @@ namespace osu.Game.Rulesets.Catch.UI public Catcher Catcher { get => catcher; - set - { - if (catcher != null) - Remove(catcher); - - Add(catcher = value); - } + set => catcherContainer.Child = catcher = value; } + internal CatcherTrailDisplay CatcherTrails { get; } + + private readonly Container catcherContainer; + private readonly CatchComboDisplay comboDisplay; private Catcher catcher; @@ -45,20 +43,28 @@ namespace osu.Game.Rulesets.Catch.UI /// private int currentDirection; + // TODO: support replay rewind + private bool lastHyperDashState; + /// /// must be set before loading. /// public CatcherArea() { Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE); - Child = comboDisplay = new CatchComboDisplay + Children = new Drawable[] { - RelativeSizeAxes = Axes.None, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopLeft, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 350f }, - X = CatchPlayfield.CENTER_X + catcherContainer = new Container { RelativeSizeAxes = Axes.Both }, + CatcherTrails = new CatcherTrailDisplay(), + comboDisplay = new CatchComboDisplay + { + RelativeSizeAxes = Axes.None, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopLeft, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 350f }, + X = CatchPlayfield.CENTER_X + } }; } @@ -102,6 +108,20 @@ namespace osu.Game.Rulesets.Catch.UI base.UpdateAfterChildren(); comboDisplay.X = Catcher.X; + + if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) + CatcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + + if (Catcher.Dashing || Catcher.HyperDashing) + { + double lastTrailTime = CatcherTrails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; + double generationInterval = Catcher.HyperDashing ? 25 : 50; + + if (Time.Current - lastTrailTime >= generationInterval) + CatcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); + } + + lastHyperDashState = Catcher.HyperDashing; } public void SetCatcherPosition(float X) From 846f539428ac75afcc2fb8a1ca4f1ea933dc78f1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 19:11:08 +0900 Subject: [PATCH 391/489] Avoid usage of LINQ in last dash trail computation --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 3 +-- .../UI/CatcherTrailDisplay.cs | 24 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index bcf4b36c9c..9eb692c875 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -114,10 +114,9 @@ namespace osu.Game.Rulesets.Catch.UI if (Catcher.Dashing || Catcher.HyperDashing) { - double lastTrailTime = CatcherTrails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; double generationInterval = Catcher.HyperDashing ? 25 : 50; - if (Time.Current - lastTrailTime >= generationInterval) + if (Time.Current - CatcherTrails.LastDashTrailTime >= generationInterval) CatcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 9931ef8f64..31b7a6e0ed 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -1,8 +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.Linq; -using JetBrains.Annotations; +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; @@ -20,13 +19,11 @@ namespace osu.Game.Rulesets.Catch.UI public class CatcherTrailDisplay : SkinReloadableDrawable { /// - /// The most recent dash trail added in this container. + /// The most recent time a dash trail was added to this container. /// Only alive (not faded out) trails are considered. + /// Returns if no dash trail is alive. /// - [CanBeNull] - public CatcherTrail LastDashTrail => dashTrails.Concat(hyperDashTrails) - .OrderByDescending(trail => trail.LifetimeStart) - .FirstOrDefault(); + public double LastDashTrailTime => getLastDashTrailTime(); public Color4 HyperDashTrailsColour => hyperDashTrails.Colour; @@ -97,5 +94,18 @@ namespace osu.Game.Rulesets.Catch.UI return sprite; } + + private double getLastDashTrailTime() + { + double maxTime = double.NegativeInfinity; + + foreach (var trail in dashTrails) + maxTime = Math.Max(maxTime, trail.LifetimeStart); + + foreach (var trail in hyperDashTrails) + maxTime = Math.Max(maxTime, trail.LifetimeStart); + + return maxTime; + } } } From c741366c72a559663d0358a317f40c149e7d0e4c Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Tue, 27 Jul 2021 09:01:01 -0400 Subject: [PATCH 392/489] review modifications: change xmldocs, change reflection method name, remove reflection method returns, simplify incompat. mod list --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 4 ++-- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 10 +++++---- .../Utils/OsuHitObjectGenerationUtils.cs | 22 +++++++------------ 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 82b8959456..c94bafe6af 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -17,13 +17,13 @@ namespace osu.Game.Rulesets.Osu.Mods { public override double ScoreMultiplier => 1.06; - public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust), typeof(ModMirror) }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModMirror)).ToArray(); public void ApplyToHitObject(HitObject hitObject) { var osuObject = (OsuHitObject)hitObject; - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + OsuHitObjectGenerationUtils.ReflectVertically(osuObject); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index fd1d685fd1..9b4614c9b2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -29,14 +29,16 @@ namespace osu.Game.Rulesets.Osu.Mods switch (Reflection.Value) { case MirrorType.Horizontal: - OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject); break; + case MirrorType.Vertical: - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + OsuHitObjectGenerationUtils.ReflectVertically(osuObject); break; + case MirrorType.Both: - OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject); + OsuHitObjectGenerationUtils.ReflectVertically(osuObject); break; } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index bf55898901..fcd5fb4a31 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -106,16 +106,15 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects an OsuHitObject's position horizontally. + /// Reflects the position of the in the playfield vertically. /// - /// The OsuHitObject to be reflected. - /// The reflected OsuHitObject. - public static OsuHitObject ReflectOsuHitObjectHorizontally(OsuHitObject osuObject) + /// The object to reflect. + public static void ReflectHorizontally(OsuHitObject osuObject) { osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); if (!(osuObject is Slider slider)) - return osuObject; + return; slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); @@ -125,21 +124,18 @@ namespace osu.Game.Rulesets.Osu.Utils point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); - - return osuObject; } /// - /// Reflects an OsuHitObject's position vertically. + /// Reflects the position of the in the playfield horizontally. /// - /// The OsuHitObject to be reflected. - /// The reflected OsuHitObject. - public static OsuHitObject ReflectOsuHitObjectVertically(OsuHitObject osuObject) + /// The object to reflect. + public static void ReflectVertically(OsuHitObject osuObject) { osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); if (!(osuObject is Slider slider)) - return osuObject; + return; slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); @@ -149,8 +145,6 @@ namespace osu.Game.Rulesets.Osu.Utils point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); - - return osuObject; } } } From ed903c60eadb700b9a8005584ebb4ca8139f46be Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 27 Jul 2021 18:14:05 +0300 Subject: [PATCH 393/489] Fix code style issues and remove unused using directives --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 3 --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index c94bafe6af..007820b016 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -3,13 +3,10 @@ using System; using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.Utils; -using osuTK; namespace osu.Game.Rulesets.Osu.Mods { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index 9b4614c9b2..a59ae33523 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -2,16 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Bindables; using osu.Game.Configuration; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.Utils; -using osuTK; namespace osu.Game.Rulesets.Osu.Mods { @@ -26,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToHitObject(HitObject hitObject) { var osuObject = (OsuHitObject)hitObject; + switch (Reflection.Value) { case MirrorType.Horizontal: From 5cb02002d78a96879fb8e948c7a236a64ad07d74 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 27 Jul 2021 18:22:32 +0300 Subject: [PATCH 394/489] Fix flipped xmldoc --- osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index fcd5fb4a31..57ec51cf64 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects the position of the in the playfield vertically. + /// Reflects the position of the in the playfield horizontally. /// /// The object to reflect. public static void ReflectHorizontally(OsuHitObject osuObject) @@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects the position of the in the playfield horizontally. + /// Reflects the position of the in the playfield vertically. /// /// The object to reflect. public static void ReflectVertically(OsuHitObject osuObject) From 94877117b9868e00ec8c95c9b482d92a25f7bc35 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 27 Jul 2021 18:22:47 +0200 Subject: [PATCH 395/489] Apply changes in-line with framework changes. --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 4 +++- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 3 ++- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 5 +++-- osu.Game/Screens/Edit/EditorTable.cs | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index ddd1dfa6cd..22033f14cd 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -18,6 +18,8 @@ using osu.Game.Scoring; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; +using osu.Framework.Localisation; +using osu.Framework.Extensions.LocalisationExtensions; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -215,7 +217,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private class HeaderText : OsuSpriteText { - public HeaderText(string text) + public HeaderText(LocalisableString text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold); diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 585b5c22aa..0da48c6f88 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -13,6 +13,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Rankings.Tables { @@ -109,7 +110,7 @@ namespace osu.Game.Overlays.Rankings.Tables { private readonly bool isHighlighted; - public HeaderText(string text, bool isHighlighted) + public HeaderText(LocalisableString text, bool isHighlighted) { this.isHighlighted = isHighlighted; diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index a6969f483f..e3081491f4 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Users; using osu.Game.Scoring; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Rankings.Tables { @@ -32,7 +33,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable CreateHeader(int index, TableColumn column) { var title = column?.Header ?? string.Empty; - return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title)); + return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString())); } protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; @@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Rankings.Tables private class UserTableHeaderText : HeaderText { - public UserTableHeaderText(string text, bool isHighlighted, bool isGrade) + public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade) : base(text, isHighlighted) { Margin = new MarginPadding diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 9578b96897..77c3a2f26c 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -46,7 +48,7 @@ namespace osu.Game.Screens.Edit private class HeaderText : OsuSpriteText { - public HeaderText(string text) + public HeaderText(LocalisableString text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); From 239b38a0ab1776fa4d38bbe35dddb0ec9114f3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jul 2021 21:42:34 +0200 Subject: [PATCH 396/489] Reduce implicit conversions by using `default` --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 2 +- osu.Game/Screens/Edit/EditorTable.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 22033f14cd..fee0e62315 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -213,7 +213,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores return content.ToArray(); } - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default); private class HeaderText : OsuSpriteText { diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 0da48c6f88..f568aae59c 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable CreateHeader(int index, TableColumn column) { - var title = column?.Header ?? string.Empty; + var title = column?.Header ?? default; return new HeaderText(title, title == HighlightedColumn); } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index e3081491f4..56814244c0 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable CreateHeader(int index, TableColumn column) { - var title = column?.Header ?? string.Empty; + var title = column?.Header ?? default; return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString())); } diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 77c3a2f26c..756339405c 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit }); } - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default); private class HeaderText : OsuSpriteText { From 712bc578dcc03a8be8649bba5fd56c483f41aa05 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Tue, 27 Jul 2021 17:45:52 -0400 Subject: [PATCH 397/489] update setting name and description --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index a59ae33523..1770e79d3f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "Reflect the playfield."; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - [SettingSource("Reflection", "Change the type of reflection.")] + [SettingSource("Mirrored axes", "Choose which of the playfield's axes are mirrored.")] public Bindable Reflection { get; } = new Bindable(); public void ApplyToHitObject(HitObject hitObject) From 0bf04ece34698217162e22a40db73a603410cf27 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Jul 2021 18:13:43 +0900 Subject: [PATCH 398/489] Avoid `internal` property by using `ChildrenOfType` --- .../TestSceneHyperDashColouring.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 4524086b02..82b69716a8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Catch.Tests } } }, skin); - trails = catcherArea.CatcherTrails; + trails = catcherArea.ChildrenOfType().Single(); }); AddStep("start hyper-dash", () => diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9eb692c875..2d6d7fb8d8 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -28,12 +28,12 @@ namespace osu.Game.Rulesets.Catch.UI set => catcherContainer.Child = catcher = value; } - internal CatcherTrailDisplay CatcherTrails { get; } - private readonly Container catcherContainer; private readonly CatchComboDisplay comboDisplay; + private readonly CatcherTrailDisplay catcherTrails; + private Catcher catcher; /// @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.UI Children = new Drawable[] { catcherContainer = new Container { RelativeSizeAxes = Axes.Both }, - CatcherTrails = new CatcherTrailDisplay(), + catcherTrails = new CatcherTrailDisplay(), comboDisplay = new CatchComboDisplay { RelativeSizeAxes = Axes.None, @@ -110,14 +110,14 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) - CatcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + catcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); if (Catcher.Dashing || Catcher.HyperDashing) { double generationInterval = Catcher.HyperDashing ? 25 : 50; - if (Time.Current - CatcherTrails.LastDashTrailTime >= generationInterval) - CatcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); + if (Time.Current - catcherTrails.LastDashTrailTime >= generationInterval) + catcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); } lastHyperDashState = Catcher.HyperDashing; From a960a28d0679b74fd7c28b21e14a41941a1a5e1b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Jul 2021 19:02:24 +0900 Subject: [PATCH 399/489] Replace "end glow" terminology with "hyper-dash after-image" Because the is "end glow" is when a hyper-dash is *started*, the name was confusing. The "after-image" was already used in the code as a synonym of "end glow" inconsistently. --- .../TestSceneHyperDashColouring.cs | 8 +++---- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +-- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- .../UI/CatcherTrailDisplay.cs | 24 +++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 82b69716a8..70b2c8c82a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Tests } [Test] - public void TestCustomEndGlowColour() + public void TestCustomAfterImageColour() { var skin = new TestSkin { @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.Tests } [Test] - public void TestCustomEndGlowColourPriority() + public void TestCustomAfterImageColourPriority() { var skin = new TestSkin { @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Catch.Tests checkHyperDashFruitColour(skin, skin.HyperDashColour); } - private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) + private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedAfterImageColour = null) { CatcherTrailDisplay trails = null; Catcher catcher = null; @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour); AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); - AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); + AddAssert("catcher after-image colours are correct", () => trails.HyperDashAfterImageColour == (expectedAfterImageColour ?? expectedCatcherColour)); AddStep("finish hyper-dashing", () => { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index b42e5352dd..9fd4610e6e 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -36,8 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI public const float ALLOWED_CATCH_RANGE = 0.8f; /// - /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail - /// and end glow/after-image during a hyper-dash. + /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail and after-image during a hyper-dash. /// public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 2d6d7fb8d8..78f7e34649 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) - catcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + catcherTrails.DisplayHyperDashAfterImage(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); if (Catcher.Dashing || Catcher.HyperDashing) { diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 31b7a6e0ed..c7e576d970 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -27,13 +27,13 @@ namespace osu.Game.Rulesets.Catch.UI public Color4 HyperDashTrailsColour => hyperDashTrails.Colour; - public Color4 EndGlowSpritesColour => endGlowSprites.Colour; + public Color4 HyperDashAfterImageColour => hyperDashAfterImages.Colour; private readonly DrawablePool trailPool; private readonly Container dashTrails; private readonly Container hyperDashTrails; - private readonly Container endGlowSprites; + private readonly Container hyperDashAfterImages; public CatcherTrailDisplay() { @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.UI trailPool = new DrawablePool(30), dashTrails = new Container { RelativeSizeAxes = Axes.Both }, hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, - endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, + hyperDashAfterImages = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, }; } @@ -53,22 +53,22 @@ namespace osu.Game.Rulesets.Catch.UI base.SkinChanged(skin); hyperDashTrails.Colour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; - endGlowSprites.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; + hyperDashAfterImages.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; } /// - /// Displays a single end-glow catcher sprite. + /// Displays a hyper-dash after-image of the catcher. /// - public void DisplayEndGlow(CatcherAnimationState animationState, float x, Vector2 scale) + public void DisplayHyperDashAfterImage(CatcherAnimationState animationState, float x, Vector2 scale) { - var endGlow = createTrail(animationState, x, scale); + var trail = createTrail(animationState, x, scale); - endGlowSprites.Add(endGlow); + hyperDashAfterImages.Add(trail); - endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); - endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In); - endGlow.FadeOut(1200); - endGlow.Expire(true); + trail.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); + trail.ScaleTo(trail.Scale * 0.95f).ScaleTo(trail.Scale * 1.2f, 1200, Easing.In); + trail.FadeOut(1200); + trail.Expire(true); } public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) From 90f3611ed059dc581229756885faf799a4bebc99 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Jul 2021 19:05:48 +0900 Subject: [PATCH 400/489] Replace "sprite" variable names in `CatcherTrailDisplay` The `CatcherTrail` was originally named `CatcherTrailSprite`, but it is not a sprite anymore. --- .../UI/CatcherTrailDisplay.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index c7e576d970..abc76fc925 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -73,26 +73,26 @@ namespace osu.Game.Rulesets.Catch.UI public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) { - var sprite = createTrail(animationState, x, scale); + var trail = createTrail(animationState, x, scale); if (hyperDashing) - hyperDashTrails.Add(sprite); + hyperDashTrails.Add(trail); else - dashTrails.Add(sprite); + dashTrails.Add(trail); - sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); - sprite.Expire(true); + trail.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); + trail.Expire(true); } private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale) { - CatcherTrail sprite = trailPool.Get(); + CatcherTrail trail = trailPool.Get(); - sprite.AnimationState = animationState; - sprite.Scale = scale; - sprite.Position = new Vector2(x, 0); + trail.AnimationState = animationState; + trail.Scale = scale; + trail.Position = new Vector2(x, 0); - return sprite; + return trail; } private double getLastDashTrailTime() From 4a4d9b0dc6ef79a48c5718ae6d3e211095f0aa02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 19:19:28 +0900 Subject: [PATCH 401/489] Update description to match mania mirror implementation --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index 1770e79d3f..3faca0b01f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -13,10 +13,10 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModMirror : ModMirror, IApplicableToHitObject { - public override string Description => "Reflect the playfield."; + public override string Description => "Flip objects on the chosen axes."; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - [SettingSource("Mirrored axes", "Choose which of the playfield's axes are mirrored.")] + [SettingSource("Mirrored axes", "Choose which axes objects are mirrored over.")] public Bindable Reflection { get; } = new Bindable(); public void ApplyToHitObject(HitObject hitObject) From 58bbe9db7e3128b80a3fa694a5d6f27628e4c8d7 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 28 Jul 2021 18:21:08 +0800 Subject: [PATCH 402/489] Added muted mod --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 31 ++++--- osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs | 11 +++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 21 +++-- osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs | 11 +++ osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs | 12 +++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 37 ++++---- osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs | 11 +++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 31 ++++--- osu.Game/Overlays/MusicController.cs | 11 ++- osu.Game/Rulesets/Mods/ModMuted.cs | 93 +++++++++++++++++++ 10 files changed, 208 insertions(+), 61 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs create mode 100644 osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs create mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs create mode 100644 osu.Game/Rulesets/Mods/ModMuted.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 76863acc78..c77caf0cef 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -1,30 +1,30 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Catch.Mods; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; +using System; using System.Collections.Generic; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Catch.Replays; -using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty; -using osu.Game.Rulesets.Catch.Scoring; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using System; -using osu.Framework.Extensions.EnumExtensions; using osu.Game.Rulesets.Catch.Edit; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Skinning.Legacy; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch @@ -130,7 +130,8 @@ namespace osu.Game.Rulesets.Catch return new Mod[] { new MultiMod(new ModWindUp(), new ModWindDown()), - new CatchModFloatingFruits() + new CatchModFloatingFruits(), + new CatchModMuted(), }; default: diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs new file mode 100644 index 0000000000..4c73a6a21f --- /dev/null +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index fe736766d9..3615d7e660 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -2,22 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -27,12 +21,18 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Difficulty; using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Skinning.Legacy; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; +using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania { @@ -253,7 +253,8 @@ namespace osu.Game.Rulesets.Mania case ModType.Fun: return new Mod[] { - new MultiMod(new ModWindUp(), new ModWindDown()) + new MultiMod(new ModWindUp(), new ModWindDown()), + new ManiaModMuted(), }; default: diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs new file mode 100644 index 0000000000..e0b98e491e --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs new file mode 100644 index 0000000000..5e3ee37b61 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5f37b0d040..30f56cdc7d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,39 +1,39 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.UI; +using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; -using osu.Game.Rulesets.Osu.Scoring; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using osu.Game.Skinning; -using System; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { @@ -188,6 +188,7 @@ namespace osu.Game.Rulesets.Osu new OsuModTraceable(), new OsuModBarrelRoll(), new OsuModApproachDifferent(), + new OsuModMuted(), }; case ModType.System: diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs new file mode 100644 index 0000000000..d6bd7c4ee8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index ab5fcf6336..428ae66a31 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -1,32 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Rulesets.UI; +using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Graphics; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Difficulty; -using osu.Game.Rulesets.Taiko.Scoring; -using osu.Game.Scoring; -using System; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Edit; +using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.Taiko.Skinning.Legacy; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Skinning; @@ -149,7 +149,8 @@ namespace osu.Game.Rulesets.Taiko case ModType.Fun: return new Mod[] { - new MultiMod(new ModWindUp(), new ModWindDown()) + new MultiMod(new ModWindUp(), new ModWindDown()), + new TaikoModMuted(), }; default: diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index a15f80ca21..0325fa588f 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -12,8 +12,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; -using osu.Framework.Utils; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -419,15 +419,20 @@ namespace osu.Game.Overlays } /// - /// Resets the speed adjustments currently applied on and applies the mod adjustments if is true. + /// Resets the track adjustments currently applied on and applies the mod adjustments if is true. /// /// - /// Does not reset speed adjustments applied directly to the beatmap track. + /// Does not reset any adjustments applied directly to the beatmap track. /// public void ResetTrackAdjustments() { CurrentTrack.ResetSpeedAdjustments(); + // Adjustments other than speed should also be reset + // e.g. ModMuted may apply volume adjustments + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Balance); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Volume); + if (allowRateAdjustments) { foreach (var mod in mods.Value.OfType()) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs new file mode 100644 index 0000000000..dff3982041 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -0,0 +1,93 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModMuted : Mod + { + public override string Name => "Muted"; + public override string Acronym => "MU"; + public override IconUsage? Icon => FontAwesome.Solid.VolumeMute; + public override string Description => "Who needs music in a rhythm game?"; + public override ModType Type => ModType.Fun; + public override double ScoreMultiplier => 1; + } + + public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack + where TObject : HitObject + { + private readonly BindableNumber volumeAdjust = new BindableDouble(); + + [SettingSource("Enable metronome", "Add a metronome to help you keep track of the rhythm.")] + public BindableBool EnableMetronome { get; } = new BindableBool + { + Default = true, + Value = true + }; + + public void ApplyToTrack(ITrack track) + { + track.AddAdjustment(AdjustableProperty.Volume, volumeAdjust); + } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + if (EnableMetronome.Value) + drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + } + + public class MetronomeBeatContainer : BeatSyncedContainer + { + private readonly double firstHitTime; + + private PausableSkinnableSound sample; + + public MetronomeBeatContainer(double firstHitTime) + { + this.firstHitTime = firstHitTime; + AllowMistimedEventFiring = false; + Divisor = 1; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + int timeSignature = (int)timingPoint.TimeSignature; + + // play metronome from one measure before the first object. + if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + return; + + sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; + sample.Play(); + } + } + } +} From 22d83c75e3a612d77ab681c704aa3596c7d3834e Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 28 Jul 2021 18:32:38 +0800 Subject: [PATCH 403/489] Revert imports re-ordering Out of the scope of this PR --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 26 +++++++++--------- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 18 ++++++------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 36 ++++++++++++------------- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 32 +++++++++++----------- osu.Game/Overlays/MusicController.cs | 2 +- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index c77caf0cef..eafa1b9b9d 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -1,30 +1,30 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; using System.Collections.Generic; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; -using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty; -using osu.Game.Rulesets.Catch.Edit; -using osu.Game.Rulesets.Catch.Mods; -using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Scoring; -using osu.Game.Rulesets.Catch.Skinning.Legacy; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using System; +using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Catch.Edit; +using osu.Game.Rulesets.Catch.Skinning.Legacy; +using osu.Game.Rulesets.Edit; using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 3615d7e660..f4b6e10af4 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -2,16 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; -using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -21,18 +27,12 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Difficulty; using osu.Game.Rulesets.Mania.Edit; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Skinning.Legacy; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; +using osu.Game.Skinning; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 30f56cdc7d..21a49f9495 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,39 +1,39 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Skinning; +using System; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 428ae66a31..adc924ba38 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -1,32 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Graphics; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Difficulty; -using osu.Game.Rulesets.Taiko.Edit; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.Taiko.Scoring; -using osu.Game.Rulesets.Taiko.Skinning.Legacy; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using System; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Taiko.Edit; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Skinning.Legacy; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Skinning; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 0325fa588f..3978d2aa6e 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -12,8 +12,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; -using osu.Framework.Threading; using osu.Framework.Utils; +using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; From 1ed4fdd5f559554a947e1889ef06c4d3366eeaf1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 20:13:40 +0900 Subject: [PATCH 404/489] Avoid deserialisation JSON request content when error is not present (or not relevant) --- osu.Game/Online/API/APIRequest.cs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index e6bfca166e..df0901fd93 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -171,19 +171,24 @@ namespace osu.Game.Online.API WebRequest?.Abort(); - string responseString = WebRequest?.GetResponseString(); - - if (!string.IsNullOrEmpty(responseString)) + // in the case of a cancellation we don't care about whether there's an error in the response. + if (!(e is OperationCanceledException)) { - try - { - // attempt to decode a displayable error string. - var error = JsonConvert.DeserializeObject(responseString); - if (error != null) - e = new APIException(error.ErrorMessage, e); - } - catch + string responseString = WebRequest?.GetResponseString(); + + // naive check whether there's an error in the response to avoid unnecessary JSON deserialisation. + if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error""")) { + try + { + // attempt to decode a displayable error string. + var error = JsonConvert.DeserializeObject(responseString); + if (error != null) + e = new APIException(error.ErrorMessage, e); + } + catch + { + } } } From cd2a1af6de82dcb4c382890e6e176e8fbd479065 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 20:46:02 +0900 Subject: [PATCH 405/489] Fix `HubClientConnector` reconnecting with no delay on server-triggered error --- osu.Game/Online/HubClientConnector.cs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 3839762e46..90049a6501 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -116,10 +116,7 @@ namespace osu.Game.Online } catch (Exception e) { - Logger.Log($"{clientName} connection error: {e}", LoggingTarget.Network); - - // retry on any failure. - await Task.Delay(5000, cancellationToken).ConfigureAwait(false); + await handleErrorAndDelay(e, cancellationToken).ConfigureAwait(false); } } } @@ -129,6 +126,15 @@ namespace osu.Game.Online } } + /// + /// Handles an exception and delays an async flow. + /// + private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken) + { + Logger.Log($"{clientName} connection error: {exception}", LoggingTarget.Network); + await Task.Delay(5000, cancellationToken).ConfigureAwait(false); + } + private HubConnection buildConnection(CancellationToken cancellationToken) { var builder = new HubConnectionBuilder() @@ -155,17 +161,18 @@ namespace osu.Game.Online return newConnection; } - private Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) + private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) { isConnected.Value = false; - Logger.Log(ex != null ? $"{clientName} lost connection: {ex}" : $"{clientName} disconnected", LoggingTarget.Network); + if (ex != null) + await handleErrorAndDelay(ex, cancellationToken).ConfigureAwait(false); + else + Logger.Log($"{clientName} disconnected", LoggingTarget.Network); // make sure a disconnect wasn't triggered (and this is still the active connection). if (!cancellationToken.IsCancellationRequested) - Task.Run(connect, default); - - return Task.CompletedTask; + await Task.Run(connect, default).ConfigureAwait(false); } private async Task disconnect(bool takeLock) From e89f33483dd796572fc89db8d267bb1a23d05c2a Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 28 Jul 2021 21:52:01 +0800 Subject: [PATCH 406/489] Code formatting fixes --- osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs | 1 + osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs | 1 + osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs index 4c73a6a21f..6d2565440a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs @@ -1,5 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. + using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Mods; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs index e0b98e491e..33ebcf303a 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs @@ -1,5 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. + using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs index d6bd7c4ee8..0f1e0b2885 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs @@ -1,5 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. + using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Objects; From fbd02dc8301446a380e479b9ed19a71ac116ee54 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 28 Jul 2021 18:24:29 +0200 Subject: [PATCH 407/489] Update framework. --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3a9fdfebab..1c180a6b39 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4baaf89c67..2efcfeb278 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 66cae06713..2630614c03 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 3a5324c94794af9e3985c2ef128cc80e66295bf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 23:58:57 +0900 Subject: [PATCH 408/489] Fix aborting an `APIRequest` potentially resulting in incorrect success --- osu.Game/Online/API/APIRequest.cs | 90 +++++++++++++------------------ 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index df0901fd93..e117293ce6 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -86,8 +86,6 @@ namespace osu.Game.Online.API /// private APIRequestCompletionState completionState; - private Action pendingFailure; - public void Perform(IAPIProvider api) { if (!(api is APIAccess apiAccess)) @@ -99,29 +97,23 @@ namespace osu.Game.Online.API API = apiAccess; User = apiAccess.LocalUser.Value; - if (checkAndScheduleFailure()) - return; + if (isFailing) return; WebRequest = CreateWebRequest(); WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}"); - if (checkAndScheduleFailure()) - return; + if (isFailing) return; - if (!WebRequest.Aborted) // could have been aborted by a Cancel() call - { - Logger.Log($@"Performing request {this}", LoggingTarget.Network); - WebRequest.Perform(); - } + Logger.Log($@"Performing request {this}", LoggingTarget.Network); + WebRequest.Perform(); - if (checkAndScheduleFailure()) - return; + if (isFailing) return; PostProcess(); - API.Schedule(TriggerSuccess); + TriggerSuccess(); } /// @@ -141,7 +133,10 @@ namespace osu.Game.Online.API completionState = APIRequestCompletionState.Completed; } - Success?.Invoke(); + if (API == null) + Success?.Invoke(); + else + API.Schedule(() => Success?.Invoke()); } internal void TriggerFailure(Exception e) @@ -154,7 +149,10 @@ namespace osu.Game.Online.API completionState = APIRequestCompletionState.Failed; } - Failure?.Invoke(e); + if (API == null) + Failure?.Invoke(e); + else + API.Schedule(() => Failure?.Invoke(e)); } public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); @@ -163,59 +161,47 @@ namespace osu.Game.Online.API { lock (completionStateLock) { - // while it doesn't matter if code following this check is run more than once, - // this avoids unnecessarily performing work where we are already sure the user has been informed. if (completionState != APIRequestCompletionState.Waiting) return; - } - WebRequest?.Abort(); + WebRequest?.Abort(); - // in the case of a cancellation we don't care about whether there's an error in the response. - if (!(e is OperationCanceledException)) - { - string responseString = WebRequest?.GetResponseString(); - - // naive check whether there's an error in the response to avoid unnecessary JSON deserialisation. - if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error""")) + // in the case of a cancellation we don't care about whether there's an error in the response. + if (!(e is OperationCanceledException)) { - try - { - // attempt to decode a displayable error string. - var error = JsonConvert.DeserializeObject(responseString); - if (error != null) - e = new APIException(error.ErrorMessage, e); - } - catch + string responseString = WebRequest?.GetResponseString(); + + // naive check whether there's an error in the response to avoid unnecessary JSON deserialisation. + if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error""")) { + try + { + // attempt to decode a displayable error string. + var error = JsonConvert.DeserializeObject(responseString); + if (error != null) + e = new APIException(error.ErrorMessage, e); + } + catch + { + } } } - } - Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network); - pendingFailure = () => TriggerFailure(e); - checkAndScheduleFailure(); + Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network); + TriggerFailure(e); + } } /// - /// Checked for cancellation or error. Also queues up the Failed event if we can. + /// Whether this request is in a failing or failed state. /// - /// Whether we are in a failed or cancelled state. - private bool checkAndScheduleFailure() + private bool isFailing { - lock (completionStateLock) + get { - if (pendingFailure == null) + lock (completionStateLock) return completionState == APIRequestCompletionState.Failed; } - - if (API == null) - pendingFailure(); - else - API.Schedule(pendingFailure); - - pendingFailure = null; - return true; } private class DisplayableError From 0620cd130e6427b3c468c03b8a4a0a19a1854b42 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 14:41:47 +0800 Subject: [PATCH 409/489] Change mod description --- osu.Game/Rulesets/Mods/ModMuted.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index dff3982041..aa2006cf73 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Muted"; public override string Acronym => "MU"; public override IconUsage? Icon => FontAwesome.Solid.VolumeMute; - public override string Description => "Who needs music in a rhythm game?"; + public override string Description => "Can you still feel the rhythm without music?"; public override ModType Type => ModType.Fun; public override double ScoreMultiplier => 1; } From 18e760ee91916bcca6071a51cdca628498dd8439 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 14:52:18 +0800 Subject: [PATCH 410/489] Extract metronome from `OsuModTarget` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 45 +--------------- .../Rulesets/Mods/MetronomeBeatContainer.cs | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 44 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index e21d1da009..576cc2da13 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; @@ -16,7 +14,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -29,7 +26,6 @@ using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.Utils; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -341,46 +337,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new TargetBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); - } - - public class TargetBeatContainer : BeatSyncedContainer - { - private readonly double firstHitTime; - - private PausableSkinnableSound sample; - - public TargetBeatContainer(double firstHitTime) - { - this.firstHitTime = firstHitTime; - AllowMistimedEventFiring = false; - Divisor = 1; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) - }; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - if (!IsBeatSyncedWithTrack) return; - - int timeSignature = (int)timingPoint.TimeSignature; - - // play metronome from one measure before the first object. - if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) - return; - - sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; - sample.Play(); - } + drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } #endregion diff --git a/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs b/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs new file mode 100644 index 0000000000..c5bb2c918f --- /dev/null +++ b/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mods +{ + public class MetronomeBeatContainer : BeatSyncedContainer + { + private readonly double firstHitTime; + + private PausableSkinnableSound sample; + + /// Start time of the first hit object, used for providing a count down. + public MetronomeBeatContainer(double firstHitTime) + { + this.firstHitTime = firstHitTime; + AllowMistimedEventFiring = false; + Divisor = 1; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + int timeSignature = (int)timingPoint.TimeSignature; + + // play metronome from one measure before the first object. + if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + return; + + sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; + sample.Play(); + } + } +} From 0196141335b11d0f5b21d0bf700eb86e1024bd0f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 14:52:40 +0800 Subject: [PATCH 411/489] Remove unused constants --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 576cc2da13..615700e6b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -63,11 +63,6 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const float distance_cap = 380f; - // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. - // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 192; - private const byte border_distance_y = 144; - /// /// The extent of rotation towards playfield centre when a circle is near the edge /// From 935984d20016d19ee0de9e057a19ae0f3f137a44 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 15:17:21 +0800 Subject: [PATCH 412/489] Rename `MetronomeBeatContainer` to `Metronome` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- .../Rulesets/Mods/{MetronomeBeatContainer.cs => Metronome.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game/Rulesets/Mods/{MetronomeBeatContainer.cs => Metronome.cs} (93%) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 615700e6b9..210d5e0403 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -332,7 +332,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } #endregion diff --git a/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs b/osu.Game/Rulesets/Mods/Metronome.cs similarity index 93% rename from osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs rename to osu.Game/Rulesets/Mods/Metronome.cs index c5bb2c918f..ee0a42b4bc 100644 --- a/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs +++ b/osu.Game/Rulesets/Mods/Metronome.cs @@ -11,14 +11,14 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { - public class MetronomeBeatContainer : BeatSyncedContainer + public class Metronome : BeatSyncedContainer { private readonly double firstHitTime; private PausableSkinnableSound sample; /// Start time of the first hit object, used for providing a count down. - public MetronomeBeatContainer(double firstHitTime) + public Metronome(double firstHitTime) { this.firstHitTime = firstHitTime; AllowMistimedEventFiring = false; From 89e8296eb1b17a42eae1f24422bf099a76a1ac37 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 15:39:26 +0800 Subject: [PATCH 413/489] Reset all types of adjustments in `MusicController`; Rename `AllowRateAdjustments` to `AllowTrackAdjustments` --- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/MusicController.cs | 23 +++++++++++-------- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/IOsuScreen.cs | 4 ++-- osu.Game/Screens/Menu/MainMenu.cs | 2 +- .../Spectate/MultiSpectatorScreen.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/StartupScreen.cs | 2 +- 9 files changed, 22 insertions(+), 19 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8e32b2e6a7..3cfa2cc755 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1058,7 +1058,7 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); - MusicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments; + MusicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments; if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index a15f80ca21..8fd50c3df2 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -400,35 +400,38 @@ namespace osu.Game.Overlays NextTrack(); } - private bool allowRateAdjustments; + private bool allowTrackAdjustments; /// - /// Whether mod rate adjustments are allowed to be applied. + /// Whether mod track adjustments are allowed to be applied. /// - public bool AllowRateAdjustments + public bool AllowTrackAdjustments { - get => allowRateAdjustments; + get => allowTrackAdjustments; set { - if (allowRateAdjustments == value) + if (allowTrackAdjustments == value) return; - allowRateAdjustments = value; + allowTrackAdjustments = value; ResetTrackAdjustments(); } } /// - /// Resets the speed adjustments currently applied on and applies the mod adjustments if is true. + /// Resets the adjustments currently applied on and applies the mod adjustments if is true. /// /// - /// Does not reset speed adjustments applied directly to the beatmap track. + /// Does not reset any adjustments applied directly to the beatmap track. /// public void ResetTrackAdjustments() { - CurrentTrack.ResetSpeedAdjustments(); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Balance); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Frequency); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Tempo); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Volume); - if (allowRateAdjustments) + if (allowTrackAdjustments) { foreach (var mod in mods.Value.OfType()) mod.ApplyToTrack(CurrentTrack); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index b6dc97a7f6..61a3b0f5cc 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit public override bool DisallowExternalBeatmapRulesetChanges => true; - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash; diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 0434135547..17384c161c 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -59,9 +59,9 @@ namespace osu.Game.Screens Bindable Ruleset { get; } /// - /// Whether mod rate adjustments are allowed to be applied. + /// Whether mod track adjustments are allowed to be applied. /// - bool AllowRateAdjustments { get; } + bool AllowTrackAdjustments { get; } /// /// Invoked when the back button has been pressed to close any overlays before exiting this . diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e53b46f391..1d0182a945 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Menu public override bool AllowExternalScreenChange => true; - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; private Screen songSelect; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 2a2759e0dd..56ed7a9564 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public override bool DisallowExternalBeatmapRulesetChanges => true; // We are managing our own adjustments. For now, this happens inside the Player instances themselves. - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; /// /// Whether all spectating players have finished loading. diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index c3b2612e79..e3fe14a585 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - public virtual bool AllowRateAdjustments => true; + public virtual bool AllowTrackAdjustments => true; public Bindable Beatmap { get; private set; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0e4d38660b..d76d44767a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; // We are managing our own adjustments (see OnEntering/OnExiting). - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; private readonly IBindable gameActive = new Bindable(true); diff --git a/osu.Game/Screens/StartupScreen.cs b/osu.Game/Screens/StartupScreen.cs index e5e134fd39..15f75d7cff 100644 --- a/osu.Game/Screens/StartupScreen.cs +++ b/osu.Game/Screens/StartupScreen.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens public override bool CursorVisible => false; - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; } From 81f23e111100ca6ee6a5a4858ec9b63c01560a7f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Jul 2021 17:12:01 +0900 Subject: [PATCH 414/489] Manage catcher trails by lifetime entries --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 6 +- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 44 +++++++--- .../UI/CatcherTrailAnimation.cs | 12 +++ .../UI/CatcherTrailDisplay.cs | 80 ++++++++++++------- .../UI/CatcherTrailEntry.cs | 31 +++++++ 5 files changed, 133 insertions(+), 40 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs create mode 100644 osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 78f7e34649..21bf049ccc 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -110,14 +110,14 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) - catcherTrails.DisplayHyperDashAfterImage(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterimage); if (Catcher.Dashing || Catcher.HyperDashing) { double generationInterval = Catcher.HyperDashing ? 25 : 50; if (Time.Current - catcherTrails.LastDashTrailTime >= generationInterval) - catcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); + displayCatcherTrail(Catcher.HyperDashing ? CatcherTrailAnimation.HyperDashing : CatcherTrailAnimation.Dashing); } lastHyperDashState = Catcher.HyperDashing; @@ -173,5 +173,7 @@ namespace osu.Game.Rulesets.Catch.UI break; } } + + private void displayCatcherTrail(CatcherTrailAnimation animation) => catcherTrails.Add(new CatcherTrailEntry(Time.Current, Catcher.CurrentState, Catcher.X, Catcher.BodyScale, animation)); } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index ff1a7d7a61..a7a2941a7a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -2,8 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Pooling; using osu.Framework.Timing; +using osu.Game.Rulesets.Objects.Pooling; using osuTK; namespace osu.Game.Rulesets.Catch.UI @@ -12,13 +12,8 @@ namespace osu.Game.Rulesets.Catch.UI /// A trail of the catcher. /// It also represents a hyper dash afterimage. /// - public class CatcherTrail : PoolableDrawable + public class CatcherTrail : PoolableDrawableWithLifetime { - public CatcherAnimationState AnimationState - { - set => body.AnimationState.Value = value; - } - private readonly SkinnableCatcher body; public CatcherTrail() @@ -34,11 +29,40 @@ namespace osu.Game.Rulesets.Catch.UI }; } - protected override void FreeAfterUse() + protected override void OnApply(CatcherTrailEntry entry) + { + Scale = entry.Scale; + Position = new Vector2(entry.Position, 0); + Alpha = 1; + + body.AnimationState.Value = entry.CatcherState; + + using (BeginAbsoluteSequence(entry.LifetimeStart, false)) + applyTransforms(entry.Animation); + } + + protected override void OnFree(CatcherTrailEntry entry) { ClearTransforms(); - Alpha = 1; - base.FreeAfterUse(); + } + + private void applyTransforms(CatcherTrailAnimation animation) + { + switch (animation) + { + case CatcherTrailAnimation.Dashing: + case CatcherTrailAnimation.HyperDashing: + this.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); + break; + + case CatcherTrailAnimation.HyperDashAfterimage: + this.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); + this.ScaleTo(Scale * 0.95f).ScaleTo(Scale * 1.2f, 1200, Easing.In); + this.FadeOut(1200); + break; + } + + Expire(); } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs new file mode 100644 index 0000000000..3ea99badc1 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Catch.UI +{ + public enum CatcherTrailAnimation + { + Dashing, + HyperDashing, + HyperDashAfterimage + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index abc76fc925..f451f84c95 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -2,12 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Skinning; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.UI @@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Represents a component responsible for displaying /// the appropriate catcher trails when requested to. /// - public class CatcherTrailDisplay : SkinReloadableDrawable + public class CatcherTrailDisplay : PooledDrawableWithLifetimeContainer { /// /// The most recent time a dash trail was added to this container. @@ -29,12 +30,17 @@ namespace osu.Game.Rulesets.Catch.UI public Color4 HyperDashAfterImageColour => hyperDashAfterImages.Colour; + protected override bool RemoveRewoundEntry => true; + private readonly DrawablePool trailPool; private readonly Container dashTrails; private readonly Container hyperDashTrails; private readonly Container hyperDashAfterImages; + [Resolved] + private ISkinSource skin { get; set; } + public CatcherTrailDisplay() { RelativeSizeAxes = Axes.Both; @@ -48,50 +54,60 @@ namespace osu.Game.Rulesets.Catch.UI }; } - protected override void SkinChanged(ISkinSource skin) + protected override void LoadComplete() { - base.SkinChanged(skin); + base.LoadComplete(); + skin.SourceChanged += skinSourceChanged; + skinSourceChanged(); + } + + private void skinSourceChanged() + { hyperDashTrails.Colour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; hyperDashAfterImages.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; } - /// - /// Displays a hyper-dash after-image of the catcher. - /// - public void DisplayHyperDashAfterImage(CatcherAnimationState animationState, float x, Vector2 scale) + protected override void AddDrawable(CatcherTrailEntry entry, CatcherTrail drawable) { - var trail = createTrail(animationState, x, scale); + switch (entry.Animation) + { + case CatcherTrailAnimation.Dashing: + dashTrails.Add(drawable); + break; - hyperDashAfterImages.Add(trail); + case CatcherTrailAnimation.HyperDashing: + hyperDashTrails.Add(drawable); + break; - trail.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); - trail.ScaleTo(trail.Scale * 0.95f).ScaleTo(trail.Scale * 1.2f, 1200, Easing.In); - trail.FadeOut(1200); - trail.Expire(true); + case CatcherTrailAnimation.HyperDashAfterimage: + hyperDashAfterImages.Add(drawable); + break; + } } - public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) + protected override void RemoveDrawable(CatcherTrailEntry entry, CatcherTrail drawable) { - var trail = createTrail(animationState, x, scale); + switch (entry.Animation) + { + case CatcherTrailAnimation.Dashing: + dashTrails.Remove(drawable); + break; - if (hyperDashing) - hyperDashTrails.Add(trail); - else - dashTrails.Add(trail); + case CatcherTrailAnimation.HyperDashing: + hyperDashTrails.Remove(drawable); + break; - trail.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); - trail.Expire(true); + case CatcherTrailAnimation.HyperDashAfterimage: + hyperDashAfterImages.Remove(drawable); + break; + } } - private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale) + protected override CatcherTrail GetDrawable(CatcherTrailEntry entry) { CatcherTrail trail = trailPool.Get(); - - trail.AnimationState = animationState; - trail.Scale = scale; - trail.Position = new Vector2(x, 0); - + trail.Apply(entry); return trail; } @@ -107,5 +123,13 @@ namespace osu.Game.Rulesets.Catch.UI return maxTime; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (skin != null) + skin.SourceChanged -= skinSourceChanged; + } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs new file mode 100644 index 0000000000..3a40ab26cc --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Performance; +using osuTK; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class CatcherTrailEntry : LifetimeEntry + { + public readonly CatcherAnimationState CatcherState; + + public readonly float Position; + + /// + /// The scaling of the catcher body. It also represents a flipped catcher (negative x component). + /// + public readonly Vector2 Scale; + + public readonly CatcherTrailAnimation Animation; + + public CatcherTrailEntry(double startTime, CatcherAnimationState catcherState, float position, Vector2 scale, CatcherTrailAnimation animation) + { + LifetimeStart = startTime; + CatcherState = catcherState; + Position = position; + Scale = scale; + Animation = animation; + } + } +} From a204ef39dd1663de2e191d29e8e96cd1ca772b2a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Jul 2021 17:32:20 +0900 Subject: [PATCH 415/489] Prevent catcher trail generation while rewinding --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 21bf049ccc..e99d33cd1b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -109,7 +109,15 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; - if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) + if (Time.Elapsed <= 0) + { + // This is probably a wrong value, but currently the true value is not recorded. + // Setting `true` will prevent generation of false-positive after-images (with more false-positives). + lastHyperDashState = true; + return; + } + + if (!lastHyperDashState && Catcher.HyperDashing) displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterimage); if (Catcher.Dashing || Catcher.HyperDashing) From 888e8f1c8052c2abef891f60eecd18c079881eed Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 21:18:07 +0800 Subject: [PATCH 416/489] Use shared metronome class --- osu.Game/Rulesets/Mods/ModMuted.cs | 47 +----------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index aa2006cf73..9e69bc1386 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -2,19 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; -using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { @@ -48,46 +42,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { if (EnableMetronome.Value) - drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); - } - - public class MetronomeBeatContainer : BeatSyncedContainer - { - private readonly double firstHitTime; - - private PausableSkinnableSound sample; - - public MetronomeBeatContainer(double firstHitTime) - { - this.firstHitTime = firstHitTime; - AllowMistimedEventFiring = false; - Divisor = 1; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) - }; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - if (!IsBeatSyncedWithTrack) return; - - int timeSignature = (int)timingPoint.TimeSignature; - - // play metronome from one measure before the first object. - if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) - return; - - sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; - sample.Play(); - } + drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } } } From 5c5e33f4d744676baa7116aec7b576e950d61e41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 01:53:08 +0900 Subject: [PATCH 417/489] Split out common logic for tournament game host tests --- .../NonVisual/CustomTourneyDirectoryTest.cs | 32 +++--------------- .../NonVisual/IPCLocationTest.cs | 28 ++-------------- .../NonVisual/TournamentHostTest.cs | 33 +++++++++++++++++++ 3 files changed, 41 insertions(+), 52 deletions(-) create mode 100644 osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs index d2369056e1..fcc9f44f0c 100644 --- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs @@ -1,21 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.IO; -using System.Threading; -using System.Threading.Tasks; using NUnit.Framework; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Platform; -using osu.Game.Tournament.Configuration; using osu.Game.Tests; +using osu.Game.Tournament.Configuration; namespace osu.Game.Tournament.Tests.NonVisual { [TestFixture] - public class CustomTourneyDirectoryTest + public class CustomTourneyDirectoryTest : TournamentHostTest { [Test] public void TestDefaultDirectory() @@ -24,7 +21,7 @@ namespace osu.Game.Tournament.Tests.NonVisual { try { - var osu = loadOsu(host); + var osu = LoadTournament(host); var storage = osu.Dependencies.Get(); Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default"))); @@ -54,7 +51,7 @@ namespace osu.Game.Tournament.Tests.NonVisual try { - var osu = loadOsu(host); + var osu = LoadTournament(host); storage = osu.Dependencies.Get(); @@ -111,7 +108,7 @@ namespace osu.Game.Tournament.Tests.NonVisual try { - var osu = loadOsu(host); + var osu = LoadTournament(host); var storage = osu.Dependencies.Get(); @@ -151,25 +148,6 @@ namespace osu.Game.Tournament.Tests.NonVisual } } - private TournamentGameBase loadOsu(GameHost host) - { - var osu = new TournamentGameBase(); - Task.Run(() => host.Run(osu)) - .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); - waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); - return osu; - } - - private static void waitForOrAssert(Func result, string failureMessage, int timeout = 90000) - { - Task task = Task.Run(() => - { - while (!result()) Thread.Sleep(200); - }); - - Assert.IsTrue(task.Wait(timeout), failureMessage); - } - private string basePath(string testInstance) => Path.Combine(RuntimeInfo.StartupDirectory, "headless", testInstance); } } diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs index e4eb5a36fb..eaa009c180 100644 --- a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs @@ -1,10 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.IO; -using System.Threading; -using System.Threading.Tasks; using NUnit.Framework; using osu.Framework; using osu.Framework.Allocation; @@ -15,7 +12,7 @@ using osu.Game.Tournament.IPC; namespace osu.Game.Tournament.Tests.NonVisual { [TestFixture] - public class IPCLocationTest + public class IPCLocationTest : TournamentHostTest { [Test] public void CheckIPCLocation() @@ -34,11 +31,11 @@ namespace osu.Game.Tournament.Tests.NonVisual try { - var osu = loadOsu(host); + var osu = LoadTournament(host); TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get(); FileBasedIPC ipc = null; - waitForOrAssert(() => (ipc = osu.Dependencies.Get() as FileBasedIPC) != null, @"ipc could not be populated in a reasonable amount of time"); + WaitForOrAssert(() => (ipc = osu.Dependencies.Get() as FileBasedIPC) != null, @"ipc could not be populated in a reasonable amount of time"); Assert.True(ipc.SetIPCLocation(testStableInstallDirectory)); Assert.True(storage.AllTournaments.Exists("stable.json")); @@ -51,24 +48,5 @@ namespace osu.Game.Tournament.Tests.NonVisual } } } - - private TournamentGameBase loadOsu(GameHost host) - { - var osu = new TournamentGameBase(); - Task.Run(() => host.Run(osu)) - .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); - waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); - return osu; - } - - private static void waitForOrAssert(Func result, string failureMessage, int timeout = 90000) - { - Task task = Task.Run(() => - { - while (!result()) Thread.Sleep(200); - }); - - Assert.IsTrue(task.Wait(timeout), failureMessage); - } } } diff --git a/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs b/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs new file mode 100644 index 0000000000..b14684200f --- /dev/null +++ b/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Platform; + +namespace osu.Game.Tournament.Tests.NonVisual +{ + public abstract class TournamentHostTest + { + public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase tournament = null) + { + tournament ??= new TournamentGameBase(); + Task.Run(() => host.Run(tournament)) + .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); + WaitForOrAssert(() => tournament.IsLoaded, @"osu! failed to start in a reasonable amount of time"); + return tournament; + } + + public static void WaitForOrAssert(Func result, string failureMessage, int timeout = 90000) + { + Task task = Task.Run(() => + { + while (!result()) Thread.Sleep(200); + }); + + Assert.IsTrue(task.Wait(timeout), failureMessage); + } + } +} From 0b8ca667a94c5c5d962bbd66726e464deca21422 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 01:53:25 +0900 Subject: [PATCH 418/489] Add failing test coverage of loading with an unavailable ruleset --- .../NonVisual/DataLoadTest.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs diff --git a/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs b/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs new file mode 100644 index 0000000000..692cb3870c --- /dev/null +++ b/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Platform; +using osu.Game.Rulesets; +using osu.Game.Tests; + +namespace osu.Game.Tournament.Tests.NonVisual +{ + public class DataLoadTest : TournamentHostTest + { + [Test] + public void TestUnavailableRuleset() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUnavailableRuleset))) + { + try + { + var osu = new TestTournament(); + + LoadTournament(host, osu); + var storage = osu.Dependencies.Get(); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default"))); + } + finally + { + host.Exit(); + } + } + } + + public class TestTournament : TournamentGameBase + { + [BackgroundDependencyLoader] + private void load() + { + Ruleset.Value = new RulesetInfo(); // not available + } + } + } +} From 46c72334fbd3d8427a92960cdffbc83d260439fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 01:54:30 +0900 Subject: [PATCH 419/489] Fix stack overflow in ruleset change rejection logic --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index a7fac24351..f2d575550a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -479,7 +479,7 @@ namespace osu.Game if (r.NewValue?.Available != true) { // reject the change if the ruleset is not available. - Ruleset.Value = r.OldValue; + Ruleset.Value = r.OldValue?.Available == true ? r.OldValue : RulesetStore.AvailableRulesets.First(); return; } From ceec74aacaa709e907b64c59833f999981ab28aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 02:00:07 +0900 Subject: [PATCH 420/489] Avoid throwing / logging an error when `drawings.txt` is missing --- .../Screens/Drawings/Components/StorageBackedTeamList.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs index f96ec01cbb..5d035a4028 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs @@ -27,6 +27,9 @@ namespace osu.Game.Tournament.Screens.Drawings.Components { var teams = new List(); + if (!storage.Exists(teams_filename)) + return teams; + try { using (Stream stream = storage.GetStream(teams_filename, FileAccess.Read, FileMode.Open)) From 77b354bfba5ad6ecf4675a359a8a6b45359b16a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 02:12:03 +0900 Subject: [PATCH 421/489] Resolve ruleset from store after loading tournament ladder --- osu.Game.Tournament/TournamentGameBase.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 92eb7ac713..531da00faf 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -66,7 +66,9 @@ namespace osu.Game.Tournament } ladder ??= new LadderInfo(); - ladder.Ruleset.Value ??= RulesetStore.AvailableRulesets.First(); + + ladder.Ruleset.Value = RulesetStore.GetRuleset(ladder.Ruleset.Value?.ShortName) + ?? RulesetStore.AvailableRulesets.First(); bool addedInfo = false; From 59a33b5d028fb688bb86956abe1e3eeac83a59c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jul 2021 18:46:49 +0200 Subject: [PATCH 422/489] Uncouple display logic from text in rankings overlay tables --- .../Rankings/Tables/CountriesTable.cs | 14 ++++---- .../Rankings/Tables/PerformanceTable.cs | 4 +-- .../Overlays/Rankings/Tables/RankingsTable.cs | 32 ++++++++++++------- .../Overlays/Rankings/Tables/ScoresTable.cs | 8 ++--- .../Rankings/Tables/UserBasedTable.cs | 32 +++++++++++-------- 5 files changed, 50 insertions(+), 40 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index c5e413c7fa..b28bf07adc 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -19,14 +19,14 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateAdditionalHeaders() => new[] + protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), + new RankingsTableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; protected override Country GetCountry(CountryStatistics item) => item.Country; diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 1e6b2307e0..ddf4bdcfeb 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -15,9 +15,9 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateUniqueHeaders() => new[] + protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index f568aae59c..0cdff0a77f 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -55,29 +55,24 @@ namespace osu.Game.Overlays.Rankings.Tables rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground { Height = row_height })); - Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); + Columns = mainHeaders.Concat(CreateAdditionalHeaders()).Cast().ToArray(); Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); } private Drawable[] createContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray(); - private static TableColumn[] mainHeaders => new[] + private static RankingsTableColumn[] mainHeaders => new[] { - new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 40)), // place - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension()), // flag and username (country name) + new RankingsTableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 40)), // place + new RankingsTableColumn(string.Empty, Anchor.CentreLeft, new Dimension()), // flag and username (country name) }; - protected abstract TableColumn[] CreateAdditionalHeaders(); + protected abstract RankingsTableColumn[] CreateAdditionalHeaders(); protected abstract Drawable[] CreateAdditionalContent(TModel item); - protected virtual string HighlightedColumn => @"Performance"; - - protected override Drawable CreateHeader(int index, TableColumn column) - { - var title = column?.Header ?? default; - return new HeaderText(title, title == HighlightedColumn); - } + protected sealed override Drawable CreateHeader(int index, TableColumn column) + => (column as RankingsTableColumn)?.CreateHeaderText() ?? new HeaderText(column?.Header ?? default, false); protected abstract Country GetCountry(TModel item); @@ -106,6 +101,19 @@ namespace osu.Game.Overlays.Rankings.Tables } }; + protected class RankingsTableColumn : TableColumn + { + protected readonly bool Highlighted; + + public RankingsTableColumn(LocalisableString? header = null, Anchor anchor = Anchor.TopLeft, Dimension dimension = null, bool highlighted = false) + : base(header, anchor, dimension) + { + Highlighted = highlighted; + } + + public virtual HeaderText CreateHeaderText() => new HeaderText(Header, Highlighted); + } + protected class HeaderText : OsuSpriteText { private readonly bool isHighlighted; diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 9fae8e1897..508ad51112 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -15,10 +15,10 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateUniqueHeaders() => new[] + protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)) + new RankingsTableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true) }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] @@ -32,7 +32,5 @@ namespace osu.Game.Overlays.Rankings.Tables Text = $@"{item.RankedScore:N0}", } }; - - protected override string HighlightedColumn => @"Ranked Score"; } } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 56814244c0..131ee86d44 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -22,20 +22,14 @@ namespace osu.Game.Overlays.Rankings.Tables protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; - protected override TableColumn[] CreateAdditionalHeaders() => new[] + protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }.Concat(CreateUniqueHeaders()) - .Concat(GradeColumns.Select(grade => new TableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)))) + .Concat(GradeColumns.Select(grade => new GradeTableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)))) .ToArray(); - protected override Drawable CreateHeader(int index, TableColumn column) - { - var title = column?.Header ?? default; - return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString())); - } - protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; protected sealed override Drawable CreateFlagContent(UserStatistics item) @@ -61,19 +55,29 @@ namespace osu.Game.Overlays.Rankings.Tables new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.A]:N0}", } }).ToArray(); - protected abstract TableColumn[] CreateUniqueHeaders(); + protected abstract RankingsTableColumn[] CreateUniqueHeaders(); protected abstract Drawable[] CreateUniqueContent(UserStatistics item); - private class UserTableHeaderText : HeaderText + private class GradeTableColumn : RankingsTableColumn { - public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade) + public GradeTableColumn(LocalisableString? header = null, Anchor anchor = Anchor.TopLeft, Dimension dimension = null, bool highlighted = false) + : base(header, anchor, dimension, highlighted) + { + } + + public override HeaderText CreateHeaderText() => new GradeHeaderText(Header, Highlighted); + } + + private class GradeHeaderText : HeaderText + { + public GradeHeaderText(LocalisableString text, bool isHighlighted) : base(text, isHighlighted) { Margin = new MarginPadding { // Grade columns have extra horizontal padding for readibility - Horizontal = isGrade ? 20 : 10, + Horizontal = 20, Vertical = 5 }; } From 0691c0dd63d26238396c602332fc12fd90c4688e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Jun 2021 19:37:09 +0200 Subject: [PATCH 423/489] Switch `Colour{Display,Palette}` to use `Colour4` --- .../UserInterface/TestSceneLabelledColourPalette.cs | 10 +++++----- osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs | 8 +++----- osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs | 3 +-- .../Graphics/UserInterfaceV2/LabelledColourPalette.cs | 4 ++-- osu.Game/Screens/Edit/Setup/ColoursSection.cs | 3 ++- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index 826da17ca8..d7af47a835 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -53,15 +53,15 @@ namespace osu.Game.Tests.Visual.UserInterface component.Colours.AddRange(new[] { - Color4.DarkRed, - Color4.Aquamarine, - Color4.Goldenrod, - Color4.Gainsboro + Colour4.DarkRed, + Colour4.Aquamarine, + Colour4.Goldenrod, + Colour4.Gainsboro }); }); } - private Color4 randomColour() => new Color4( + private Colour4 randomColour() => new Color4( RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 01d91f7cfd..dfdc76687f 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -11,22 +10,21 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { /// /// A component which displays a colour along with related description text. /// - public class ColourDisplay : CompositeDrawable, IHasCurrentValue + public class ColourDisplay : CompositeDrawable, IHasCurrentValue { - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); private Box fill; private OsuSpriteText colourHexCode; private OsuSpriteText colourName; - public Bindable Current + public Bindable Current { get => current.Current; set => current.Current = value; diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index ba950048dc..a32257ef79 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -17,7 +16,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// public class ColourPalette : CompositeDrawable { - public BindableList Colours { get; } = new BindableList(); + public BindableList Colours { get; } = new BindableList(); private string colourNamePrefix = "Colour"; diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs index 58443953bc..8970ef1115 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK.Graphics; +using osu.Framework.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -13,7 +13,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { } - public BindableList Colours => Component.Colours; + public BindableList Colours => Component.Colours; public string ColourNamePrefix { diff --git a/osu.Game/Screens/Edit/Setup/ColoursSection.cs b/osu.Game/Screens/Edit/Setup/ColoursSection.cs index 4a81959a54..d7e16645f2 100644 --- a/osu.Game/Screens/Edit/Setup/ColoursSection.cs +++ b/osu.Game/Screens/Edit/Setup/ColoursSection.cs @@ -2,6 +2,7 @@ // 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.Graphics; using osu.Framework.Localisation; @@ -32,7 +33,7 @@ namespace osu.Game.Screens.Edit.Setup var colours = Beatmap.BeatmapSkin?.GetConfig>(GlobalSkinColours.ComboColours)?.Value; if (colours != null) - comboColours.Colours.AddRange(colours); + comboColours.Colours.AddRange(colours.Select(c => (Colour4)c)); } } } From c8891d4504145b07e97b8f34b7bb9e5f68e912c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Jun 2021 20:20:46 +0200 Subject: [PATCH 424/489] Integrate editor colour display with colour picker & popover --- .../Graphics/UserInterfaceV2/ColourDisplay.cs | 19 +++++++++++++++--- .../Graphics/UserInterfaceV2/ColourPalette.cs | 20 ++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index dfdc76687f..25f89fdcaa 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -3,11 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; @@ -16,7 +19,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// /// A component which displays a colour along with related description text. /// - public class ColourDisplay : CompositeDrawable, IHasCurrentValue + public class ColourDisplay : CompositeDrawable, IHasCurrentValue, IHasPopover { private readonly BindableWithCurrent current = new BindableWithCurrent(); @@ -60,10 +63,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - new CircularContainer + new OsuClickableContainer { RelativeSizeAxes = Axes.X, Height = 100, + CornerRadius = 50, Masking = true, Children = new Drawable[] { @@ -77,7 +81,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 Origin = Anchor.Centre, Font = OsuFont.Default.With(size: 12) } - } + }, + Action = this.ShowPopover }, colourName = new OsuSpriteText { @@ -101,5 +106,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 colourHexCode.Text = current.Value.ToHex(); colourHexCode.Colour = OsuColour.ForegroundTextColourFor(current.Value); } + + public Popover GetPopover() => new OsuPopover(false) + { + Child = new OsuColourPicker + { + Current = { BindTarget = Current } + } + }; } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index a32257ef79..d8edd00c16 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.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.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -72,14 +73,17 @@ namespace osu.Game.Graphics.UserInterfaceV2 { base.LoadComplete(); - Colours.BindCollectionChanged((_, __) => updatePalette(), true); + Colours.BindCollectionChanged((_, args) => updatePalette(args), true); FinishTransforms(true); } private const int fade_duration = 200; - private void updatePalette() + private void updatePalette(NotifyCollectionChangedEventArgs args) { + if (args.Action == NotifyCollectionChangedAction.Replace) + return; + palette.Clear(); if (Colours.Any()) @@ -93,12 +97,18 @@ namespace osu.Game.Graphics.UserInterfaceV2 placeholder.FadeIn(fade_duration, Easing.OutQuint); } - foreach (var item in Colours) + for (int i = 0; i < Colours.Count; ++i) { - palette.Add(new ColourDisplay + // copy to avoid accesses to modified closure. + int colourIndex = i; + ColourDisplay display; + + palette.Add(display = new ColourDisplay { - Current = { Value = item } + Current = { Value = Colours[colourIndex] } }); + + display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); } reindexItems(); From 3a347188a5f20e79b6bdaeb16d79cbcd432efcb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 13:21:26 +0900 Subject: [PATCH 425/489] Allow `LinkFlowContainer` to still open external URLs when `OsuGame` is not available --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 5ff2fdf6b2..85ef779e48 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Framework.Platform; using osu.Game.Graphics.Sprites; using osu.Game.Users; @@ -25,6 +26,9 @@ namespace osu.Game.Graphics.Containers [Resolved(CanBeNull = true)] private OsuGame game { get; set; } + [Resolved] + private GameHost host { get; set; } + public void AddLinks(string text, List links) { if (string.IsNullOrEmpty(text) || links == null) @@ -91,8 +95,11 @@ namespace osu.Game.Graphics.Containers { if (action != null) action(); - else - game?.HandleLink(link); + else if (game != null) + game.HandleLink(link); + // fallback to handle cases where OsuGame is not available, ie. tournament client. + else if (link.Action == LinkAction.External) + host.OpenUrlExternally(link.Argument); }, }); } From 6249ce0ea3ebbd12718b3688a3d3dc9aff6bea43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 13:21:50 +0900 Subject: [PATCH 426/489] Add a warning and link for more information on `drawings.txt` population --- .../Screens/Drawings/DrawingsScreen.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 4c3adeae76..d02e0ebf86 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -9,11 +9,13 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Drawings.Components; @@ -51,6 +53,29 @@ namespace osu.Game.Tournament.Screens.Drawings if (!TeamList.Teams.Any()) { + LinkFlowContainer links; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Height = 0.3f, + }, + new WarningBox("No drawings.txt file found. Please create one and restart the client."), + links = new LinkFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 60, + AutoSizeAxes = Axes.Both + } + }; + + links.AddLink("Click for details on the file format", "https://osu.ppy.sh/wiki/en/Tournament_Drawings", t => t.Colour = Color4.White); return; } From 665bd3690aa67f3746e8bd0d5f0ddd2a05907ff4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 15:27:24 +0900 Subject: [PATCH 427/489] Add a few cases of missing `ConfigureAwait` calls in tests project --- .../Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs | 4 ++-- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 42848ffc0c..7e7e5ebc45 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -168,8 +168,8 @@ namespace osu.Game.Tests.Online public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) { - await AllowImport.Task; - return await (CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)); + await AllowImport.Task.ConfigureAwait(false); + return await (CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)).ConfigureAwait(false); } } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 449401c0bf..66ac700c51 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -143,9 +143,9 @@ namespace osu.Game.Tests.Visual.SongSelect public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) { if (blockCalculation) - await calculationBlocker.Task; + await calculationBlocker.Task.ConfigureAwait(false); - return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); + return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken).ConfigureAwait(false); } } } From 451c65a2c894e05f57b03a593fc3d8ccfc740828 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 29 Jul 2021 23:41:01 -0700 Subject: [PATCH 428/489] Fix song progress graph not being correctly hidden --- osu.Game/Screens/Play/SongProgress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index bd861dc598..f28622f42e 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Play float barHeight = bottom_bar_height + handle_size.Y; bar.ResizeHeightTo(ShowGraph.Value ? barHeight + graph_height : barHeight, transition_duration, Easing.In); - graph.MoveToY(ShowGraph.Value ? 0 : bottom_bar_height + graph_height, transition_duration, Easing.In); + graph.FadeTo(ShowGraph.Value ? 1 : 0, transition_duration, Easing.In); updateInfoMargin(); } From 4e2f928d65ab6d962cc8026ade8473dbad4c206f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 30 Jul 2021 15:44:09 +0900 Subject: [PATCH 429/489] Fix comment --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index e99d33cd1b..9422d6d51b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Catch.UI if (Time.Elapsed <= 0) { // This is probably a wrong value, but currently the true value is not recorded. - // Setting `true` will prevent generation of false-positive after-images (with more false-positives). + // Setting `true` will prevent generation of false-positive after-images (with more false-negatives). lastHyperDashState = true; return; } From a2f3edbfc0cfda19ce03fa388ab0436c96d43904 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 15:49:11 +0900 Subject: [PATCH 430/489] Fade track volume out as combo increases --- osu.Game/Rulesets/Mods/ModMuted.cs | 48 ++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 9e69bc1386..a39d798bc4 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -1,14 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { @@ -22,10 +26,15 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; } - public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack + public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack, IApplicableToScoreProcessor where TObject : HitObject { - private readonly BindableNumber volumeAdjust = new BindableDouble(); + private readonly BindableNumber trackVolumeAdjust = new BindableDouble(0.5); + private readonly BindableNumber metronomeVolumeAdjust = new BindableDouble(0.5); + + private BindableNumber currentCombo; + + private AudioContainer metronomeContainer; [SettingSource("Enable metronome", "Add a metronome to help you keep track of the rhythm.")] public BindableBool EnableMetronome { get; } = new BindableBool @@ -34,15 +43,44 @@ namespace osu.Game.Rulesets.Mods Value = true }; + [SettingSource("Muted at combo", "The combo count at which point the music is completely muted.")] + public BindableInt MuteComboCount { get; } = new BindableInt + { + Default = 100, + Value = 100, + MinValue = 0, + MaxValue = 500, + }; + public void ApplyToTrack(ITrack track) { - track.AddAdjustment(AdjustableProperty.Volume, volumeAdjust); + track.AddAdjustment(AdjustableProperty.Volume, trackVolumeAdjust); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - if (EnableMetronome.Value) - drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + if (!EnableMetronome.Value) return; + + drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer + { + Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) + }); + + metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); } + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + currentCombo = scoreProcessor.Combo.GetBoundCopy(); + currentCombo.BindValueChanged(combo => + { + double dimFactor = Math.Min(1, (double)combo.NewValue / MuteComboCount.Value); + + metronomeVolumeAdjust.Value = dimFactor; + trackVolumeAdjust.Value = 1 - dimFactor; + }, true); + } + + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; } } From b399ddaea047ce22092566f46685bc984f5bfcc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:10:10 +0900 Subject: [PATCH 431/489] Add inverse setting --- osu.Game/Rulesets/Mods/ModMuted.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index a39d798bc4..5fc9ab1eda 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -52,6 +52,13 @@ namespace osu.Game.Rulesets.Mods MaxValue = 500, }; + [SettingSource("Start muted", "Increase volume as combo builds.")] + public BindableBool InverseMuting { get; } = new BindableBool + { + Default = false, + Value = false + }; + public void ApplyToTrack(ITrack track) { track.AddAdjustment(AdjustableProperty.Volume, trackVolumeAdjust); @@ -78,6 +85,8 @@ namespace osu.Game.Rulesets.Mods metronomeVolumeAdjust.Value = dimFactor; trackVolumeAdjust.Value = 1 - dimFactor; + if (InverseMuting.Value) + dimFactor = 1 - dimFactor; }, true); } From 3cfd235b7f53b14c63fadd21b4775ad865c971c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:10:20 +0900 Subject: [PATCH 432/489] Add tween when missing to avoid sudden volume difference --- osu.Game/Rulesets/Mods/ModMuted.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 5fc9ab1eda..469f3073d6 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; @@ -43,7 +44,7 @@ namespace osu.Game.Rulesets.Mods Value = true }; - [SettingSource("Muted at combo", "The combo count at which point the music is completely muted.")] + [SettingSource("Final volume at combo", "The combo count at which point the music reaches its final volume.")] public BindableInt MuteComboCount { get; } = new BindableInt { Default = 100, @@ -83,10 +84,19 @@ namespace osu.Game.Rulesets.Mods { double dimFactor = Math.Min(1, (double)combo.NewValue / MuteComboCount.Value); - metronomeVolumeAdjust.Value = dimFactor; - trackVolumeAdjust.Value = 1 - dimFactor; if (InverseMuting.Value) dimFactor = 1 - dimFactor; + + if (combo.NewValue < combo.OldValue) + { + scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 200, Easing.OutQuint); + scoreProcessor.TransformBindableTo(trackVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); + } + else + { + metronomeVolumeAdjust.Value = dimFactor; + trackVolumeAdjust.Value = 1 - dimFactor; + } }, true); } From 0c3f1195e91329c4dce028797c085c2354f63657 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:47:07 +0900 Subject: [PATCH 433/489] Allow audio adjustments to be applied to `DrawableRuleset`s --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 43 +++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index daf46dcdcc..b3242a8b4b 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -1,29 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Cursor; using osu.Game.Input.Handlers; using osu.Game.Overlays; using osu.Game.Replays; using osu.Game.Rulesets.Configuration; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; @@ -98,6 +98,14 @@ namespace osu.Game.Rulesets.UI private DrawableRulesetDependencies dependencies; + /// + /// An audio container which can be used to apply adjustments to playfield content. + /// + /// + /// Does not affect . + /// + public AudioContainer AudioContainer { get; private set; } + /// /// Creates a ruleset visualisation for the provided ruleset and beatmap. /// @@ -155,21 +163,22 @@ namespace osu.Game.Rulesets.UI [BackgroundDependencyLoader] private void load(CancellationToken? cancellationToken) { - InternalChildren = new Drawable[] + InternalChild = frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) { - frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) + FrameStablePlayback = FrameStablePlayback, + Children = new Drawable[] { - FrameStablePlayback = FrameStablePlayback, - Children = new Drawable[] + FrameStableComponents, + AudioContainer = new AudioContainer { - FrameStableComponents, - KeyBindingInputManager + RelativeSizeAxes = Axes.Both, + Child = KeyBindingInputManager .WithChild(CreatePlayfieldAdjustmentContainer() .WithChild(Playfield) ), - Overlays, - } - }, + }, + Overlays, + } }; if ((ResumeOverlay = CreateResumeOverlay()) != null) From bdc5eb6d3dcd9a4e75e1cd301886cd163d4560ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:46:42 +0900 Subject: [PATCH 434/489] Add ability to also mute hitsounds --- osu.Game/Rulesets/Mods/ModMuted.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 469f3073d6..e29c1166e4 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack, IApplicableToScoreProcessor where TObject : HitObject { - private readonly BindableNumber trackVolumeAdjust = new BindableDouble(0.5); + private readonly BindableNumber mainVolumeAdjust = new BindableDouble(0.5); private readonly BindableNumber metronomeVolumeAdjust = new BindableDouble(0.5); private BindableNumber currentCombo; @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mods Value = true }; - [SettingSource("Final volume at combo", "The combo count at which point the music reaches its final volume.")] + [SettingSource("Final volume at combo", "The combo count at which point the track reaches its final volume.")] public BindableInt MuteComboCount { get; } = new BindableInt { Default = 100, @@ -60,9 +60,16 @@ namespace osu.Game.Rulesets.Mods Value = false }; + [SettingSource("Mute hit sounds", "Hit sounds are also muted alongside the track.")] + public BindableBool AffectsHitSounds { get; } = new BindableBool + { + Default = false, + Value = false + }; + public void ApplyToTrack(ITrack track) { - track.AddAdjustment(AdjustableProperty.Volume, trackVolumeAdjust); + track.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) @@ -75,6 +82,9 @@ namespace osu.Game.Rulesets.Mods }); metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + + if (AffectsHitSounds.Value) + drawableRuleset.AudioContainer.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -90,12 +100,12 @@ namespace osu.Game.Rulesets.Mods if (combo.NewValue < combo.OldValue) { scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 200, Easing.OutQuint); - scoreProcessor.TransformBindableTo(trackVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); + scoreProcessor.TransformBindableTo(mainVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); } else { metronomeVolumeAdjust.Value = dimFactor; - trackVolumeAdjust.Value = 1 - dimFactor; + mainVolumeAdjust.Value = 1 - dimFactor; } }, true); } From d5e68f53b5e005bf93d359f6e81521d17dbd15d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 17:38:04 +0900 Subject: [PATCH 435/489] Change some defaults and always tween --- osu.Game/Rulesets/Mods/ModMuted.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index e29c1166e4..0ec45f4fbb 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods private AudioContainer metronomeContainer; - [SettingSource("Enable metronome", "Add a metronome to help you keep track of the rhythm.")] + [SettingSource("Enable metronome", "Add a metronome beat to help you keep track of the rhythm.")] public BindableBool EnableMetronome { get; } = new BindableBool { Default = true, @@ -63,8 +63,8 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Mute hit sounds", "Hit sounds are also muted alongside the track.")] public BindableBool AffectsHitSounds { get; } = new BindableBool { - Default = false, - Value = false + Default = true, + Value = true }; public void ApplyToTrack(ITrack track) @@ -97,16 +97,8 @@ namespace osu.Game.Rulesets.Mods if (InverseMuting.Value) dimFactor = 1 - dimFactor; - if (combo.NewValue < combo.OldValue) - { - scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 200, Easing.OutQuint); - scoreProcessor.TransformBindableTo(mainVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); - } - else - { - metronomeVolumeAdjust.Value = dimFactor; - mainVolumeAdjust.Value = 1 - dimFactor; - } + scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 500, Easing.OutQuint); + scoreProcessor.TransformBindableTo(mainVolumeAdjust, 1 - dimFactor, 500, Easing.OutQuint); }, true); } From 185ea776f5e1f88855fbde040b27848bfc6e82c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 18:11:35 +0900 Subject: [PATCH 436/489] Fix incorrect authorisation loss exception handling with recent changes --- osu.Game/Online/API/APIAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index eb3abff2b7..b35dfa11cb 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -150,7 +150,7 @@ namespace osu.Game.Online.API userReq.Failure += ex => { - if (ex.InnerException is WebException webException && webException.Message == @"Unauthorized") + if (ex is WebException webException && webException.Message == @"Unauthorized") { log.Add(@"Login no longer valid"); Logout(); From cd516c4ac7644689e9fdd8b8f3758ed06a9547de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 19:38:43 +0900 Subject: [PATCH 437/489] Fix regressed metronome handling --- osu.Game/Rulesets/Mods/ModMuted.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 0ec45f4fbb..5454483571 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -74,14 +74,15 @@ namespace osu.Game.Rulesets.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - if (!EnableMetronome.Value) return; - - drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer + if (EnableMetronome.Value) { - Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) - }); + drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer + { + Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) + }); - metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + } if (AffectsHitSounds.Value) drawableRuleset.AudioContainer.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); From fcfa6d5bd10773ae4a5533de6166b4b95c4b7a21 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 14:17:43 +0200 Subject: [PATCH 438/489] Localise rankings overlay header. --- .../Rankings/RankingsOverlayHeader.cs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 92e22f5873..4edd8ab1a7 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -5,6 +5,9 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Rulesets; using osu.Game.Users; +using osu.Game.Resources.Localisation.Web; +using osu.Framework.Localisation; +using System; namespace osu.Game.Overlays.Rankings { @@ -29,13 +32,14 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = "ranking"; + Title = PageTitleStrings.MainRankingControllerDefault; Description = "find out who's the best right now"; IconTexture = "Icons/Hexacons/rankings"; } } } + [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] public enum RankingsScope { Performance, @@ -43,4 +47,28 @@ namespace osu.Game.Overlays.Rankings Score, Country } + + public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsScope value) + { + switch (value) + { + case RankingsScope.Performance: + return RankingsStrings.TypePerformance; + + case RankingsScope.Spotlights: + return RankingsStrings.TypeCharts; + + case RankingsScope.Score: + return RankingsStrings.TypeScore; + + case RankingsScope.Country: + return RankingsStrings.TypeCountry; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } } From 9515a67f57ff5ae73100c75c6a4ce042fa047449 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 14:35:25 +0200 Subject: [PATCH 439/489] Localise ranking sort tab control. --- .../Rankings/RankingsSortTabControl.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs index c0bbf46e30..782055181f 100644 --- a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs +++ b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs @@ -1,19 +1,42 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.Rankings { public class RankingsSortTabControl : OverlaySortTabControl { public RankingsSortTabControl() { - Title = "Show"; + Title = RankingsStrings.FilterTitle; } } + [LocalisableEnum(typeof(RankingsSortCriteriaEnumLocalisationMapper))] public enum RankingsSortCriteria { All, Friends } + + public class RankingsSortCriteriaEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsSortCriteria value) + { + switch (value) + { + case RankingsSortCriteria.All: + return SortStrings.All; + + case RankingsSortCriteria.Friends: + return SortStrings.Friends; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } } From be3c02ff7ffef7454e7246b587f86af3e5b39ddc Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 29 Jul 2021 20:29:11 +0900 Subject: [PATCH 440/489] Remove 'Soft' select sample variant usage (soft is the new default) --- osu.Game/Graphics/UserInterface/HoverSampleSet.cs | 2 -- .../Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs | 2 +- osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs | 2 +- osu.Game/Screens/Edit/EditorTable.cs | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs index b4afb4831f..e7b8625175 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs @@ -10,8 +10,6 @@ namespace osu.Game.Graphics.UserInterface [Description("default")] Default, - [Description("soft")] - Soft, [Description("button")] Button, diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs index 794c728e56..618c7dabfa 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs @@ -32,7 +32,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Depth = 1 }, - new HoverClickSounds(HoverSampleSet.Soft) + new HoverClickSounds() }); } diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index e4c78e723d..3d09d09833 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Depth = 1 }, - new HoverClickSounds(HoverSampleSet.Soft) + new HoverClickSounds() }); } diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 756339405c..d0a83e94a1 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -67,7 +67,6 @@ namespace osu.Game.Screens.Edit private EditorClock clock { get; set; } public RowBackground(object item) - : base(HoverSampleSet.Soft) { Item = item; From c1d8a7e2ad6b3d7aeb5794048c55dd4e8f04566f Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:23:49 +0900 Subject: [PATCH 441/489] Add and use 'Submit' select sample variant for particular components --- osu.Game/Graphics/UserInterface/DialogButton.cs | 1 + osu.Game/Graphics/UserInterface/HoverSampleSet.cs | 2 ++ osu.Game/Online/Chat/DrawableLinkCompiler.cs | 1 + osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs | 1 + osu.Game/Overlays/News/NewsCard.cs | 2 ++ .../Overlays/Profile/Sections/BeatmapMetadataContainer.cs | 2 ++ osu.Game/Screens/Edit/EditorTable.cs | 1 - osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs | 2 ++ osu.Game/Users/Drawables/ClickableAvatar.cs | 6 ++++++ osu.Game/Users/UserPanel.cs | 1 + 10 files changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 2d75dad828..2f9e4dae51 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -56,6 +56,7 @@ namespace osu.Game.Graphics.UserInterface private Vector2 hoverSpacing => new Vector2(3f, 0f); public DialogButton() + : base(HoverSampleSet.Submit) { RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs index e7b8625175..a5ea6fcfbf 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs @@ -10,6 +10,8 @@ namespace osu.Game.Graphics.UserInterface [Description("default")] Default, + [Description("submit")] + Submit, [Description("button")] Button, diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index 53ea1d6f99..4df60eba69 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -31,6 +31,7 @@ namespace osu.Game.Online.Chat protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); public DrawableLinkCompiler(IEnumerable parts) + : base(HoverSampleSet.Submit) { Parts = parts.ToList(); } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index afb5eeda36..9ff39ce1dd 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -50,6 +50,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected Action ViewBeatmap; protected BeatmapPanel(BeatmapSetInfo setInfo) + : base(HoverSampleSet.Submit) { Debug.Assert(setInfo.OnlineBeatmapSetID != null); diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index 599b45fa78..cc2fa7e1e1 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -14,6 +14,7 @@ using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.News @@ -28,6 +29,7 @@ namespace osu.Game.Overlays.News private TextFlowContainer main; public NewsCard(APINewsPost post) + : base(HoverSampleSet.Submit) { this.post = post; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 67a976fe6f..a8a4cfc365 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile.Sections { @@ -17,6 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections private readonly BeatmapInfo beatmap; protected BeatmapMetadataContainer(BeatmapInfo beatmap) + : base(HoverSampleSet.Submit) { this.beatmap = beatmap; diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index d0a83e94a1..ab8bd6a3bc 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -11,7 +11,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK.Graphics; diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index 845c0a914e..6ecb96f723 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Select.Options { @@ -76,6 +77,7 @@ namespace osu.Game.Screens.Select.Options public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); public BeatmapOptionsButton() + : base(HoverSampleSet.Submit) { Width = width; RelativeSizeAxes = Axes.Y; diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index f73489ac61..c8af8d80e4 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Users.Drawables { @@ -71,6 +72,11 @@ namespace osu.Game.Users.Drawables { private LocalisableString tooltip = default_tooltip_text; + public ClickableArea() + : base(HoverSampleSet.Submit) + { + } + public override LocalisableString TooltipText { get => Enabled.Value ? tooltip : default; diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 0981136dba..ff0d03a036 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -31,6 +31,7 @@ namespace osu.Game.Users protected Drawable Background { get; private set; } protected UserPanel(User user) + : base(HoverSampleSet.Submit) { if (user == null) throw new ArgumentNullException(nameof(user)); From 7dc1de74233897eb49f2a0c76b7d8e1042aa598d Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:25:44 +0900 Subject: [PATCH 442/489] Use 'Submit' select sample variant for back button --- osu.Game/Graphics/UserInterface/BackButton.cs | 2 +- osu.Game/Graphics/UserInterface/TwoLayerButton.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index b941e5fcbd..b8196c6360 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -20,7 +20,7 @@ namespace osu.Game.Graphics.UserInterface { Size = TwoLayerButton.SIZE_EXTENDED; - Child = button = new TwoLayerButton + Child = button = new TwoLayerButton(HoverSampleSet.Submit) { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 8f03c7073c..969309bc79 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -71,7 +71,8 @@ namespace osu.Game.Graphics.UserInterface } } - public TwoLayerButton() + public TwoLayerButton(HoverSampleSet sampleSet = HoverSampleSet.Default) + : base(sampleSet) { Size = SIZE_RETRACTED; Shear = shear; From 9b7bb3724444930716569814d73aa8d9124a3e16 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:32:08 +0900 Subject: [PATCH 443/489] Add hover+select sounds to some components that are missing them --- .../Containers/OsuRearrangeableListItem.cs | 47 ++++++++++--------- .../UserInterface/ExternalLinkButton.cs | 16 +++++-- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 19 +++++++- .../Chat/Tabs/PrivateChannelTabItem.cs | 7 ++- .../Settings/Sections/Input/KeyBindingRow.cs | 4 +- osu.Game/Users/Drawables/UpdateableFlag.cs | 11 ++++- 6 files changed, 70 insertions(+), 34 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs b/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs index 911d47704a..d43c3a608b 100644 --- a/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs +++ b/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; @@ -59,33 +60,37 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load() { - InternalChild = new GridContainer + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] + new GridContainer { - new[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { - handleContainer = new Container + new[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 5 }, - Child = handle = new PlaylistItemHandle + handleContainer = new Container { - Size = new Vector2(12), - Colour = HandleColour, - AlwaysPresent = true, - Alpha = 0 - } - }, - CreateContent() - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 5 }, + Child = handle = new PlaylistItemHandle + { + Size = new Vector2(12), + Colour = HandleColour, + AlwaysPresent = true, + Alpha = 0 + } + }, + CreateContent() + } + }, + ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } }, - ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, - RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } + new HoverClickSounds() }; } diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs index 6ad88eaaba..0df69a5b54 100644 --- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs +++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs @@ -23,14 +23,20 @@ namespace osu.Game.Graphics.UserInterface [Resolved] private GameHost host { get; set; } + private readonly SpriteIcon linkIcon; + public ExternalLinkButton(string link = null) { Link = link; Size = new Vector2(12); - InternalChild = new SpriteIcon + InternalChildren = new Drawable[] { - Icon = FontAwesome.Solid.ExternalLinkAlt, - RelativeSizeAxes = Axes.Both + linkIcon = new SpriteIcon + { + Icon = FontAwesome.Solid.ExternalLinkAlt, + RelativeSizeAxes = Axes.Both + }, + new HoverClickSounds(HoverSampleSet.Submit) }; } @@ -42,13 +48,13 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { - InternalChild.FadeColour(hoverColour, 500, Easing.OutQuint); + linkIcon.FadeColour(hoverColour, 500, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - InternalChild.FadeColour(Color4.White, 500, Easing.OutQuint); + linkIcon.FadeColour(Color4.White, 500, Easing.OutQuint); base.OnHoverLost(e); } diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index cca4dc33e5..58b402c164 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -3,6 +3,9 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -13,6 +16,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using osuTK; using osuTK.Graphics; @@ -39,6 +43,8 @@ namespace osu.Game.Overlays.Chat.Tabs protected override Container Content => content; + private Sample sampleTabSwitched; + public ChannelTabItem(Channel value) : base(value) { @@ -112,6 +118,7 @@ namespace osu.Game.Overlays.Chat.Tabs }, }, }, + new HoverSounds() }; } @@ -152,11 +159,12 @@ namespace osu.Game.Overlays.Chat.Tabs } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { BackgroundActive = colours.ChatBlue; BackgroundInactive = colours.Gray4; backgroundHover = colours.Gray7; + sampleTabSwitched = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); highlightBox.Colour = colours.Yellow; } @@ -217,7 +225,14 @@ namespace osu.Game.Overlays.Chat.Tabs Text.Font = Text.Font.With(weight: FontWeight.Medium); } - protected override void OnActivated() => updateState(); + protected override void OnActivated() + { + if (IsLoaded) + sampleTabSwitched?.Play(); + + updateState(); + } + protected override void OnDeactivated() => updateState(); } } diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 7c82420e08..d01aec630e 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Tabs if (value.Type != ChannelType.PM) throw new ArgumentException("Argument value needs to have the targettype user!"); - ClickableAvatar avatar; + DrawableAvatar avatar; AddRange(new Drawable[] { @@ -48,10 +48,9 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, - Child = new DelayedLoadWrapper(avatar = new ClickableAvatar(value.Users.First()) + Child = new DelayedLoadWrapper(avatar = new DrawableAvatar(value.Users.First()) { - RelativeSizeAxes = Axes.Both, - OpenOnClick = false, + RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 4f7deebb5b..6e018597be 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -138,7 +138,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, } } - } + }, + new HoverClickSounds() }; foreach (var b in bindings) @@ -458,6 +459,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Origin = Anchor.Centre, Text = keyBinding.KeyCombination.ReadableString(), }, + new HoverSounds() }; } diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index 1d30720889..7db834bf83 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; namespace osu.Game.Users.Drawables @@ -32,9 +33,17 @@ namespace osu.Game.Users.Drawables if (country == null && !ShowPlaceholderOnNull) return null; - return new DrawableFlag(country) + return new Container { RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new DrawableFlag(country) + { + RelativeSizeAxes = Axes.Both + }, + new HoverClickSounds(HoverSampleSet.Submit) + } }; } From 9538c4c7f29d2d9692fb7f4259b19b1fcfb95507 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:35:07 +0900 Subject: [PATCH 444/489] Make the news collapsable month sections sound like dropdowns, because they pseudo kinda are --- .../Overlays/News/Sidebar/MonthSection.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index b300a755f9..cd6ab224a9 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -15,13 +15,18 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using System.Diagnostics; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Platform; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.News.Sidebar { public class MonthSection : CompositeDrawable { private const int animation_duration = 250; + private Sample sampleOpen; + private Sample sampleClose; public readonly BindableBool Expanded = new BindableBool(); @@ -51,6 +56,21 @@ namespace osu.Game.Overlays.News.Sidebar } } }; + + Expanded.ValueChanged += expanded => + { + if (expanded.NewValue) + sampleOpen?.Play(); + else + sampleClose?.Play(); + }; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); } private class DropdownHeader : OsuClickableContainer @@ -59,6 +79,8 @@ namespace osu.Game.Overlays.News.Sidebar private readonly SpriteIcon icon; + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); + public DropdownHeader(int month, int year) { var date = new DateTime(year, month, 1); @@ -104,6 +126,7 @@ namespace osu.Game.Overlays.News.Sidebar private readonly APINewsPost post; public PostButton(APINewsPost post) + : base(HoverSampleSet.Submit) { this.post = post; From 971728196902b9cd905e967363a7265ce71b576e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 14:51:16 +0200 Subject: [PATCH 445/489] Localise rankings Spotlight selector. --- .../Overlays/Rankings/SpotlightSelector.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 89dd4eafdd..7f642c1832 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -16,6 +16,8 @@ using System.Collections.Generic; using osu.Framework.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Rankings { @@ -92,10 +94,10 @@ namespace osu.Game.Overlays.Rankings Margin = new MarginPadding { Bottom = 5 }, Children = new Drawable[] { - startDateColumn = new InfoColumn(@"Start Date"), - endDateColumn = new InfoColumn(@"End Date"), - mapCountColumn = new InfoColumn(@"Map Count"), - participantsColumn = new InfoColumn(@"Participants") + startDateColumn = new InfoColumn(RankingsStrings.SpotlightStartDate), + endDateColumn = new InfoColumn(RankingsStrings.SpotlightEndDate), + mapCountColumn = new InfoColumn(RankingsStrings.SpotlightMapCount), + participantsColumn = new InfoColumn(RankingsStrings.SpotlightParticipants) } }, new RankingsSortTabControl @@ -123,21 +125,21 @@ namespace osu.Game.Overlays.Rankings startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); mapCountColumn.Value = response.BeatmapSets.Count.ToString(); - participantsColumn.Value = response.Spotlight.Participants?.ToString("N0"); + participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString("N0"); } - private string dateToString(DateTimeOffset date) => date.ToString("yyyy-MM-dd"); + private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString("yyyy-MM-dd"); private class InfoColumn : FillFlowContainer { - public string Value + public LocalisableString Value { set => valueText.Text = value; } private readonly OsuSpriteText valueText; - public InfoColumn(string name) + public InfoColumn(LocalisableString name) { AutoSizeAxes = Axes.Both; Direction = FillDirection.Vertical; From db1ed873e42e6a43fdf4589ee2ade961a9d86b85 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 15:24:10 +0200 Subject: [PATCH 446/489] Localise ranking tables. --- .../Rankings/Tables/CountriesTable.cs | 26 ++++++++++--------- .../Rankings/Tables/PerformanceTable.cs | 6 +++-- .../Overlays/Rankings/Tables/RankingsTable.cs | 2 +- .../Overlays/Rankings/Tables/ScoresTable.cs | 10 ++++--- .../Rankings/Tables/UserBasedTable.cs | 17 ++++++------ 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index b28bf07adc..a09629de57 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -9,6 +9,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Rankings.Tables { @@ -21,12 +23,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new RankingsTableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), - new RankingsTableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatActiveUsers, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatPlayCount, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatRankedScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatAverageScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatPerformance, Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), + new RankingsTableColumn(RankingsStrings.StatAveragePerformance, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; protected override Country GetCountry(CountryStatistics item) => item.Country; @@ -37,27 +39,27 @@ namespace osu.Game.Overlays.Rankings.Tables { new ColoredRowText { - Text = $@"{item.ActiveUsers:N0}", + Text = item.ActiveUsers.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.PlayCount:N0}", + Text = item.PlayCount.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.RankedScore:N0}", + Text = item.RankedScore.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.RankedScore / Math.Max(item.ActiveUsers, 1):N0}", + Text = (item.RankedScore / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") }, new RowText { - Text = $@"{item.Performance:N0}", + Text = item.Performance.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}", + Text = (item.Performance / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") } }; diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index ddf4bdcfeb..ae49445702 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables @@ -17,12 +19,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), + new RankingsTableColumn(RankingsStrings.StatPerformance, Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = $@"{item.PP:N0}", } + new RowText { Text = item.PP.ToLocalisableString("N0"), } }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 0cdff0a77f..59f38920d6 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Rankings.Tables private OsuSpriteText createIndexDrawable(int index) => new RowText { - Text = $"#{index + 1}", + Text = (index + 1).ToLocalisableString("\\##"), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.SemiBold) }; diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 508ad51112..4cf75eda81 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables @@ -17,19 +19,19 @@ namespace osu.Game.Overlays.Rankings.Tables protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new RankingsTableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true) + new RankingsTableColumn(RankingsStrings.StatTotalScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatRankedScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true) }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { new ColoredRowText { - Text = $@"{item.TotalScore:N0}", + Text = item.TotalScore.ToLocalisableString("N0"), }, new RowText { - Text = $@"{item.RankedScore:N0}", + Text = item.RankedScore.ToLocalisableString("N0") } }; } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 131ee86d44..a7fa6899f8 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Users; using osu.Game.Scoring; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Rankings.Tables { @@ -20,12 +21,12 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; + protected virtual IEnumerable GradeColumns => new List { RankingsStrings.Statss, RankingsStrings.Stats, RankingsStrings.Stata }; protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new RankingsTableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatAccuracy, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatPlayCount, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }.Concat(CreateUniqueHeaders()) .Concat(GradeColumns.Select(grade => new GradeTableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)))) .ToArray(); @@ -47,12 +48,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] { new ColoredRowText { Text = item.DisplayAccuracy, }, - new ColoredRowText { Text = $@"{item.PlayCount:N0}", }, + new ColoredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, }.Concat(CreateUniqueContent(item)).Concat(new[] { - new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]:N0}", }, - new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]:N0}", }, - new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.A]:N0}", } + new ColoredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, + new ColoredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, + new ColoredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } }).ToArray(); protected abstract RankingsTableColumn[] CreateUniqueHeaders(); @@ -78,7 +79,7 @@ namespace osu.Game.Overlays.Rankings.Tables { // Grade columns have extra horizontal padding for readibility Horizontal = 20, - Vertical = 5 + Vertical = 150 }; } } From 8a42d88793fe0592aa17c684f3c9e87a25c6a3ee Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 16:28:18 +0200 Subject: [PATCH 447/489] Fix whitespace inspections. --- osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 4edd8ab1a7..1d6d884d58 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Rankings { case RankingsScope.Performance: return RankingsStrings.TypePerformance; - + case RankingsScope.Spotlights: return RankingsStrings.TypeCharts; From 7e870235576dffb34f2282bc0660bb8c364ed10a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 23:57:40 +0900 Subject: [PATCH 448/489] Update resources --- 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 1c180a6b39..c1075cfb17 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2efcfeb278..936fe8db94 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 2630614c03..7b7d5f80fe 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 5381e11880be447bef613b0c1a9af7e34b5c0eb1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 19:06:25 +0200 Subject: [PATCH 449/489] Revert unintentional change. --- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index a7fa6899f8..60c3c76a34 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Rankings.Tables { // Grade columns have extra horizontal padding for readibility Horizontal = 20, - Vertical = 150 + Vertical = 5 }; } } From 652fe6c413aec6f3bd5f0e6be18d0c8d7ea1188f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 19:07:49 +0200 Subject: [PATCH 450/489] Uppercase sort filter control text. --- osu.Game/Overlays/Rankings/RankingsSortTabControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs index 782055181f..c04eb5bdd1 100644 --- a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs +++ b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Rankings { public RankingsSortTabControl() { - Title = RankingsStrings.FilterTitle; + Title = RankingsStrings.FilterTitle.ToUpper(); } } From c7e9d09ce3747a95f91e0264c3648e277bff5a32 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 19:08:27 +0200 Subject: [PATCH 451/489] Localise left over numeric value. --- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 7f642c1832..89d40277fc 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Rankings { startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); - mapCountColumn.Value = response.BeatmapSets.Count.ToString(); + mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString("N0"); participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString("N0"); } From 3409bc6b27550580e8de777ff1e88200aeddc862 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 31 Jul 2021 01:38:54 +0300 Subject: [PATCH 452/489] Update mapper usages with `LocalisableDescription` --- osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs | 52 ++++-------- .../Overlays/BeatmapListing/SearchCategory.cs | 55 ++++--------- .../Overlays/BeatmapListing/SearchExplicit.cs | 23 +----- .../Overlays/BeatmapListing/SearchExtra.cs | 22 +---- .../Overlays/BeatmapListing/SearchGeneral.cs | 26 +----- .../Overlays/BeatmapListing/SearchGenre.cs | 81 ++++++------------- .../Overlays/BeatmapListing/SearchLanguage.cs | 74 ++++------------- .../Overlays/BeatmapListing/SearchPlayed.cs | 28 ++----- .../Overlays/BeatmapListing/SortCriteria.cs | 53 ++++-------- .../Dashboard/DashboardOverlayHeader.cs | 21 +---- .../Dashboard/Friends/OnlineStatus.cs | 28 ++----- .../Dashboard/Friends/UserSortTabControl.cs | 28 ++----- .../OverlayPanelDisplayStyleControl.cs | 28 ++----- osu.Game/Scoring/ScoreRank.cs | 46 ++--------- 14 files changed, 120 insertions(+), 445 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs index 6003e23a84..edaf044466 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs @@ -1,22 +1,34 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps { - [LocalisableEnum(typeof(BeatmapSetOnlineStatusEnumLocalisationMapper))] public enum BeatmapSetOnlineStatus { None = -3, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusGraveyard))] Graveyard = -2, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusWip))] WIP = -1, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusPending))] Pending = 0, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusRanked))] Ranked = 1, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusApproved))] Approved = 2, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusQualified))] Qualified = 3, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusLoved))] Loved = 4, } @@ -25,40 +37,4 @@ namespace osu.Game.Beatmaps public static bool GrantsPerformancePoints(this BeatmapSetOnlineStatus status) => status == BeatmapSetOnlineStatus.Ranked || status == BeatmapSetOnlineStatus.Approved; } - - public class BeatmapSetOnlineStatusEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(BeatmapSetOnlineStatus value) - { - switch (value) - { - case BeatmapSetOnlineStatus.None: - return string.Empty; - - case BeatmapSetOnlineStatus.Graveyard: - return BeatmapsetsStrings.ShowStatusGraveyard; - - case BeatmapSetOnlineStatus.WIP: - return BeatmapsetsStrings.ShowStatusWip; - - case BeatmapSetOnlineStatus.Pending: - return BeatmapsetsStrings.ShowStatusPending; - - case BeatmapSetOnlineStatus.Ranked: - return BeatmapsetsStrings.ShowStatusRanked; - - case BeatmapSetOnlineStatus.Approved: - return BeatmapsetsStrings.ShowStatusApproved; - - case BeatmapSetOnlineStatus.Qualified: - return BeatmapsetsStrings.ShowStatusQualified; - - case BeatmapSetOnlineStatus.Loved: - return BeatmapsetsStrings.ShowStatusLoved; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs index 8a9df76af3..d6ae41aba1 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs @@ -1,69 +1,42 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchCategoryEnumLocalisationMapper))] public enum SearchCategory { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusAny))] Any, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusLeaderboard))] [Description("Has Leaderboard")] Leaderboard, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusRanked))] Ranked, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusQualified))] Qualified, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusLoved))] Loved, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusFavourites))] Favourites, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusPending))] [Description("Pending & WIP")] Pending, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusGraveyard))] Graveyard, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusMine))] [Description("My Maps")] Mine, } - - public class SearchCategoryEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchCategory value) - { - switch (value) - { - case SearchCategory.Any: - return BeatmapsStrings.StatusAny; - - case SearchCategory.Leaderboard: - return BeatmapsStrings.StatusLeaderboard; - - case SearchCategory.Ranked: - return BeatmapsStrings.StatusRanked; - - case SearchCategory.Qualified: - return BeatmapsStrings.StatusQualified; - - case SearchCategory.Loved: - return BeatmapsStrings.StatusLoved; - - case SearchCategory.Favourites: - return BeatmapsStrings.StatusFavourites; - - case SearchCategory.Pending: - return BeatmapsStrings.StatusPending; - - case SearchCategory.Graveyard: - return BeatmapsStrings.StatusGraveyard; - - case SearchCategory.Mine: - return BeatmapsStrings.StatusMine; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs index 78e6a4e094..80482b32a0 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs @@ -1,34 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchExplicitEnumLocalisationMapper))] public enum SearchExplicit { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.NsfwExclude))] Hide, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.NsfwInclude))] Show } - - public class SearchExplicitEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchExplicit value) - { - switch (value) - { - case SearchExplicit.Hide: - return BeatmapsStrings.NsfwExclude; - - case SearchExplicit.Show: - return BeatmapsStrings.NsfwInclude; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchExtra.cs b/osu.Game/Overlays/BeatmapListing/SearchExtra.cs index 4b3fb6e833..e54632acd8 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchExtra.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchExtra.cs @@ -1,38 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchExtraEnumLocalisationMapper))] public enum SearchExtra { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ExtraVideo))] [Description("Has Video")] Video, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ExtraStoryboard))] [Description("Has Storyboard")] Storyboard } - - public class SearchExtraEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchExtra value) - { - switch (value) - { - case SearchExtra.Video: - return BeatmapsStrings.ExtraVideo; - - case SearchExtra.Storyboard: - return BeatmapsStrings.ExtraStoryboard; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs index b4c629f7fa..d334b82e88 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs @@ -1,44 +1,24 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchGeneralEnumLocalisationMapper))] public enum SearchGeneral { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralRecommended))] [Description("Recommended difficulty")] Recommended, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralConverts))] [Description("Include converted beatmaps")] Converts, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralFollows))] [Description("Subscribed mappers")] Follows } - - public class SearchGeneralEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchGeneral value) - { - switch (value) - { - case SearchGeneral.Recommended: - return BeatmapsStrings.GeneralRecommended; - - case SearchGeneral.Converts: - return BeatmapsStrings.GeneralConverts; - - case SearchGeneral.Follows: - return BeatmapsStrings.GeneralFollows; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchGenre.cs b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs index b2709ecd2e..08855284cb 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchGenre.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs @@ -1,87 +1,56 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchGenreEnumLocalisationMapper))] public enum SearchGenre { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreAny))] Any = 0, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreUnspecified))] Unspecified = 1, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreVideoGame))] [Description("Video Game")] VideoGame = 2, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreAnime))] Anime = 3, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreRock))] Rock = 4, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenrePop))] Pop = 5, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreOther))] Other = 6, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreNovelty))] Novelty = 7, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreHipHop))] [Description("Hip Hop")] HipHop = 9, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreElectronic))] Electronic = 10, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreMetal))] Metal = 11, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreClassical))] Classical = 12, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreFolk))] Folk = 13, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreJazz))] Jazz = 14 } - - public class SearchGenreEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchGenre value) - { - switch (value) - { - case SearchGenre.Any: - return BeatmapsStrings.GenreAny; - - case SearchGenre.Unspecified: - return BeatmapsStrings.GenreUnspecified; - - case SearchGenre.VideoGame: - return BeatmapsStrings.GenreVideoGame; - - case SearchGenre.Anime: - return BeatmapsStrings.GenreAnime; - - case SearchGenre.Rock: - return BeatmapsStrings.GenreRock; - - case SearchGenre.Pop: - return BeatmapsStrings.GenrePop; - - case SearchGenre.Other: - return BeatmapsStrings.GenreOther; - - case SearchGenre.Novelty: - return BeatmapsStrings.GenreNovelty; - - case SearchGenre.HipHop: - return BeatmapsStrings.GenreHipHop; - - case SearchGenre.Electronic: - return BeatmapsStrings.GenreElectronic; - - case SearchGenre.Metal: - return BeatmapsStrings.GenreMetal; - - case SearchGenre.Classical: - return BeatmapsStrings.GenreClassical; - - case SearchGenre.Folk: - return BeatmapsStrings.GenreFolk; - - case SearchGenre.Jazz: - return BeatmapsStrings.GenreJazz; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs index fc176c305a..7ffa0282b7 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs @@ -1,117 +1,73 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchLanguageEnumLocalisationMapper))] [HasOrderedElements] public enum SearchLanguage { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageAny))] [Order(0)] Any, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageUnspecified))] [Order(14)] Unspecified, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageEnglish))] [Order(1)] English, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageJapanese))] [Order(6)] Japanese, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageChinese))] [Order(2)] Chinese, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageInstrumental))] [Order(12)] Instrumental, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageKorean))] [Order(7)] Korean, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageFrench))] [Order(3)] French, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageGerman))] [Order(4)] German, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageSwedish))] [Order(9)] Swedish, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageSpanish))] [Order(8)] Spanish, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageItalian))] [Order(5)] Italian, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageRussian))] [Order(10)] Russian, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguagePolish))] [Order(11)] Polish, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageOther))] [Order(13)] Other } - - public class SearchLanguageEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchLanguage value) - { - switch (value) - { - case SearchLanguage.Any: - return BeatmapsStrings.LanguageAny; - - case SearchLanguage.Unspecified: - return BeatmapsStrings.LanguageUnspecified; - - case SearchLanguage.English: - return BeatmapsStrings.LanguageEnglish; - - case SearchLanguage.Japanese: - return BeatmapsStrings.LanguageJapanese; - - case SearchLanguage.Chinese: - return BeatmapsStrings.LanguageChinese; - - case SearchLanguage.Instrumental: - return BeatmapsStrings.LanguageInstrumental; - - case SearchLanguage.Korean: - return BeatmapsStrings.LanguageKorean; - - case SearchLanguage.French: - return BeatmapsStrings.LanguageFrench; - - case SearchLanguage.German: - return BeatmapsStrings.LanguageGerman; - - case SearchLanguage.Swedish: - return BeatmapsStrings.LanguageSwedish; - - case SearchLanguage.Spanish: - return BeatmapsStrings.LanguageSpanish; - - case SearchLanguage.Italian: - return BeatmapsStrings.LanguageItalian; - - case SearchLanguage.Russian: - return BeatmapsStrings.LanguageRussian; - - case SearchLanguage.Polish: - return BeatmapsStrings.LanguagePolish; - - case SearchLanguage.Other: - return BeatmapsStrings.LanguageOther; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs b/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs index f24cf46c2d..3b04ac01ca 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs @@ -1,38 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchPlayedEnumLocalisationMapper))] public enum SearchPlayed { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.PlayedAny))] Any, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.PlayedPlayed))] Played, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.PlayedUnplayed))] Unplayed } - - public class SearchPlayedEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchPlayed value) - { - switch (value) - { - case SearchPlayed.Any: - return BeatmapsStrings.PlayedAny; - - case SearchPlayed.Played: - return BeatmapsStrings.PlayedPlayed; - - case SearchPlayed.Unplayed: - return BeatmapsStrings.PlayedUnplayed; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs index 5ea885eecc..871b3c162b 100644 --- a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs +++ b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs @@ -1,58 +1,35 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SortCriteriaLocalisationMapper))] public enum SortCriteria { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingTitle))] Title, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingArtist))] Artist, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingDifficulty))] Difficulty, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRanked))] Ranked, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRating))] Rating, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingPlays))] Plays, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingFavourites))] Favourites, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRelevance))] Relevance } - - public class SortCriteriaLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SortCriteria value) - { - switch (value) - { - case SortCriteria.Title: - return BeatmapsStrings.ListingSearchSortingTitle; - - case SortCriteria.Artist: - return BeatmapsStrings.ListingSearchSortingArtist; - - case SortCriteria.Difficulty: - return BeatmapsStrings.ListingSearchSortingDifficulty; - - case SortCriteria.Ranked: - return BeatmapsStrings.ListingSearchSortingRanked; - - case SortCriteria.Rating: - return BeatmapsStrings.ListingSearchSortingRating; - - case SortCriteria.Plays: - return BeatmapsStrings.ListingSearchSortingPlays; - - case SortCriteria.Favourites: - return BeatmapsStrings.ListingSearchSortingFavourites; - - case SortCriteria.Relevance: - return BeatmapsStrings.ListingSearchSortingRelevance; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 056d4ad6f7..79dadf5e0d 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -23,30 +22,12 @@ namespace osu.Game.Overlays.Dashboard } } - [LocalisableEnum(typeof(DashboardOverlayTabsEnumLocalisationMapper))] public enum DashboardOverlayTabs { + [LocalisableDescription(typeof(FriendsStrings), nameof(FriendsStrings.TitleCompact))] Friends, [Description("Currently Playing")] CurrentlyPlaying } - - public class DashboardOverlayTabsEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(DashboardOverlayTabs value) - { - switch (value) - { - case DashboardOverlayTabs.Friends: - return FriendsStrings.TitleCompact; - - case DashboardOverlayTabs.CurrentlyPlaying: - return @"Currently Playing"; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs b/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs index 4b5a7ef066..853c94d8ae 100644 --- a/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs +++ b/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs @@ -1,38 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard.Friends { - [LocalisableEnum(typeof(OnlineStatusEnumLocalisationMapper))] public enum OnlineStatus { + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusAll))] All, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOnline))] Online, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOffline))] Offline } - - public class OnlineStatusEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(OnlineStatus value) - { - switch (value) - { - case OnlineStatus.All: - return SortStrings.All; - - case OnlineStatus.Online: - return UsersStrings.StatusOnline; - - case OnlineStatus.Offline: - return UsersStrings.StatusOffline; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs b/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs index dc756e2957..7fee5f4668 100644 --- a/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs +++ b/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -12,33 +11,16 @@ namespace osu.Game.Overlays.Dashboard.Friends { } - [LocalisableEnum(typeof(UserSortCriteriaEnumLocalisationMappper))] public enum UserSortCriteria { + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.LastVisit))] [Description(@"Recently Active")] LastVisit, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Rank))] Rank, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Username))] Username } - - public class UserSortCriteriaEnumLocalisationMappper : EnumLocalisationMapper - { - public override LocalisableString Map(UserSortCriteria value) - { - switch (value) - { - case UserSortCriteria.LastVisit: - return SortStrings.LastVisit; - - case UserSortCriteria.Rank: - return SortStrings.Rank; - - case UserSortCriteria.Username: - return SortStrings.Username; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs index c2268ff43c..d7a3b052ae 100644 --- a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs +++ b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs @@ -12,7 +12,6 @@ using osu.Framework.Allocation; using osuTK.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; -using System; using osu.Game.Resources.Localisation.Web; using osu.Framework.Extensions; @@ -101,32 +100,15 @@ namespace osu.Game.Overlays } } - [LocalisableEnum(typeof(OverlayPanelDisplayStyleEnumLocalisationMapper))] public enum OverlayPanelDisplayStyle { + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ViewModeCard))] Card, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ViewModeList))] List, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ViewModeBrick))] Brick } - - public class OverlayPanelDisplayStyleEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(OverlayPanelDisplayStyle value) - { - switch (value) - { - case OverlayPanelDisplayStyle.Card: - return UsersStrings.ViewModeCard; - - case OverlayPanelDisplayStyle.List: - return UsersStrings.ViewModeList; - - case OverlayPanelDisplayStyle.Brick: - return UsersStrings.ViewModeBrick; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index f3b4551ff8..64f7da9ba3 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -1,74 +1,44 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Scoring { - [LocalisableEnum(typeof(ScoreRankEnumLocalisationMapper))] public enum ScoreRank { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankD))] [Description(@"D")] D, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankC))] [Description(@"C")] C, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankB))] [Description(@"B")] B, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankA))] [Description(@"A")] A, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankS))] [Description(@"S")] S, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankSH))] [Description(@"S+")] SH, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankX))] [Description(@"SS")] X, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankXH))] [Description(@"SS+")] XH, } - - public class ScoreRankEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(ScoreRank value) - { - switch (value) - { - case ScoreRank.XH: - return BeatmapsStrings.RankXH; - - case ScoreRank.X: - return BeatmapsStrings.RankX; - - case ScoreRank.SH: - return BeatmapsStrings.RankSH; - - case ScoreRank.S: - return BeatmapsStrings.RankS; - - case ScoreRank.A: - return BeatmapsStrings.RankA; - - case ScoreRank.B: - return BeatmapsStrings.RankB; - - case ScoreRank.C: - return BeatmapsStrings.RankC; - - case ScoreRank.D: - return BeatmapsStrings.RankD; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } From 397c73e786aa8c49ff233fc74bab2ec1bdd24657 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 31 Jul 2021 02:09:25 +0300 Subject: [PATCH 453/489] Add audio adjustment support to `Metronome` --- osu.Game/Rulesets/Mods/Metronome.cs | 62 +++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Metronome.cs b/osu.Game/Rulesets/Mods/Metronome.cs index ee0a42b4bc..8b6d86c45f 100644 --- a/osu.Game/Rulesets/Mods/Metronome.cs +++ b/osu.Game/Rulesets/Mods/Metronome.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Audio.Track; -using osu.Framework.Graphics; +using osu.Framework.Bindables; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; @@ -11,11 +11,11 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { - public class Metronome : BeatSyncedContainer + public class Metronome : BeatSyncedContainer, IAdjustableAudioComponent { private readonly double firstHitTime; - private PausableSkinnableSound sample; + private readonly PausableSkinnableSound sample; /// Start time of the first hit object, used for providing a count down. public Metronome(double firstHitTime) @@ -23,15 +23,8 @@ namespace osu.Game.Rulesets.Mods this.firstHitTime = firstHitTime; AllowMistimedEventFiring = false; Divisor = 1; - } - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) - }; + InternalChild = sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")); } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) @@ -49,5 +42,50 @@ namespace osu.Game.Rulesets.Mods sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; sample.Play(); } + + #region IAdjustableAudioComponent + + public IBindable AggregateVolume => sample.AggregateVolume; + + public IBindable AggregateBalance => sample.AggregateBalance; + + public IBindable AggregateFrequency => sample.AggregateFrequency; + + public IBindable AggregateTempo => sample.AggregateTempo; + + public BindableNumber Volume => sample.Volume; + + public BindableNumber Balance => sample.Balance; + + public BindableNumber Frequency => sample.Frequency; + + public BindableNumber Tempo => sample.Tempo; + + public void BindAdjustments(IAggregateAudioAdjustment component) + { + sample.BindAdjustments(component); + } + + public void UnbindAdjustments(IAggregateAudioAdjustment component) + { + sample.UnbindAdjustments(component); + } + + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + sample.AddAdjustment(type, adjustBindable); + } + + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + sample.RemoveAdjustment(type, adjustBindable); + } + + public void RemoveAllAdjustments(AdjustableProperty type) + { + sample.RemoveAllAdjustments(type); + } + + #endregion } } From 4c68268d98186e445ce123ee57677a8bd577bcd7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sat, 31 Jul 2021 08:31:08 +0900 Subject: [PATCH 454/489] Call `ApplyTransformsAt` on free --- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index a7a2941a7a..f3f996d2c5 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -31,9 +31,8 @@ namespace osu.Game.Rulesets.Catch.UI protected override void OnApply(CatcherTrailEntry entry) { - Scale = entry.Scale; Position = new Vector2(entry.Position, 0); - Alpha = 1; + Scale = entry.Scale; body.AnimationState.Value = entry.CatcherState; @@ -43,6 +42,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override void OnFree(CatcherTrailEntry entry) { + ApplyTransformsAt(double.MinValue); ClearTransforms(); } From e6f337a3c8c753681c8a22f91f84378db6e43e2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 14:27:20 +0900 Subject: [PATCH 455/489] User verbatim string for `ToLocalisableString` calls (and rename US spelling) --- .../Overlays/Rankings/SpotlightSelector.cs | 6 ++--- .../Rankings/Tables/CountriesTable.cs | 22 +++++++++---------- .../Rankings/Tables/PerformanceTable.cs | 2 +- .../Overlays/Rankings/Tables/RankingsTable.cs | 4 ++-- .../Overlays/Rankings/Tables/ScoresTable.cs | 2 +- .../Rankings/Tables/UserBasedTable.cs | 10 ++++----- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 89d40277fc..5309778a47 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -124,11 +124,11 @@ namespace osu.Game.Overlays.Rankings { startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); - mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString("N0"); - participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString("N0"); + mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString(@"N0"); + participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0"); } - private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString("yyyy-MM-dd"); + private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString(@"yyyy-MM-dd"); private class InfoColumn : FillFlowContainer { diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index a09629de57..85a317728f 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -37,29 +37,29 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[] { - new ColoredRowText + new ColouredRowText { - Text = item.ActiveUsers.ToLocalisableString("N0") + Text = item.ActiveUsers.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = item.PlayCount.ToLocalisableString("N0") + Text = item.PlayCount.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = item.RankedScore.ToLocalisableString("N0") + Text = item.RankedScore.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = (item.RankedScore / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") + Text = (item.RankedScore / Math.Max(item.ActiveUsers, 1)).ToLocalisableString(@"N0") }, new RowText { - Text = item.Performance.ToLocalisableString("N0") + Text = item.Performance.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = (item.Performance / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") + Text = (item.Performance / Math.Max(item.ActiveUsers, 1)).ToLocalisableString(@"N0") } }; diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index ae49445702..6facf1e7a2 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = item.PP.ToLocalisableString("N0"), } + new RowText { Text = item.PP.ToLocalisableString(@"N0"), } }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 59f38920d6..bc8eac16a9 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Rankings.Tables private OsuSpriteText createIndexDrawable(int index) => new RowText { - Text = (index + 1).ToLocalisableString("\\##"), + Text = (index + 1).ToLocalisableString(@"\##"), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.SemiBold) }; @@ -144,7 +144,7 @@ namespace osu.Game.Overlays.Rankings.Tables } } - protected class ColoredRowText : RowText + protected class ColouredRowText : RowText { [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 4cf75eda81..312c894055 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new ColoredRowText + new ColouredRowText { Text = item.TotalScore.ToLocalisableString("N0"), }, diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 60c3c76a34..ce7a0e7e79 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -47,13 +47,13 @@ namespace osu.Game.Overlays.Rankings.Tables protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] { - new ColoredRowText { Text = item.DisplayAccuracy, }, - new ColoredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, + new ColouredRowText { Text = item.DisplayAccuracy, }, + new ColouredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, }.Concat(CreateUniqueContent(item)).Concat(new[] { - new ColoredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, - new ColoredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, - new ColoredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } + new ColouredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, + new ColouredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, + new ColouredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } }).ToArray(); protected abstract RankingsTableColumn[] CreateUniqueHeaders(); From c0824989558bc389b9bee043d5ded289f3e2f48d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 14:29:38 +0900 Subject: [PATCH 456/489] Fix some missed instances of verbatim string conversion --- osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 4 ++-- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 312c894055..b6bb66e2c8 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -27,11 +27,11 @@ namespace osu.Game.Overlays.Rankings.Tables { new ColouredRowText { - Text = item.TotalScore.ToLocalisableString("N0"), + Text = item.TotalScore.ToLocalisableString(@"N0"), }, new RowText { - Text = item.RankedScore.ToLocalisableString("N0") + Text = item.RankedScore.ToLocalisableString(@"N0") } }; } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index ce7a0e7e79..b96ab556df 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -48,12 +48,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] { new ColouredRowText { Text = item.DisplayAccuracy, }, - new ColouredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, + new ColouredRowText { Text = item.PlayCount.ToLocalisableString(@"N0") }, }.Concat(CreateUniqueContent(item)).Concat(new[] { - new ColouredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, - new ColouredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, - new ColouredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } + new ColouredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString(@"N0"), }, + new ColouredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString(@"N0"), }, + new ColouredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString(@"N0"), } }).ToArray(); protected abstract RankingsTableColumn[] CreateUniqueHeaders(); From 29328bdf7f49a35f15e4bf87e9a2fd6151dd2567 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 15:03:26 +0900 Subject: [PATCH 457/489] Use metronome's audio adjustments directly --- osu.Game/Rulesets/Mods/ModMuted.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 5454483571..9eb218fd9b 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -35,8 +35,6 @@ namespace osu.Game.Rulesets.Mods private BindableNumber currentCombo; - private AudioContainer metronomeContainer; - [SettingSource("Enable metronome", "Add a metronome beat to help you keep track of the rhythm.")] public BindableBool EnableMetronome { get; } = new BindableBool { @@ -76,12 +74,11 @@ namespace osu.Game.Rulesets.Mods { if (EnableMetronome.Value) { - drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer - { - Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) - }); + Metronome metronome; - metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + drawableRuleset.Overlays.Add(metronome = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + + metronome.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); } if (AffectsHitSounds.Value) From 53c901bfa8051bab3ff96aee65ec8c16472764b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 15:05:54 +0900 Subject: [PATCH 458/489] Expose `DrawableRuleset` audio adjustments as non-container --- ...rDisplayExtensions.cs => TimeDisplayExtensions.cs} | 6 ++++++ osu.Game/Rulesets/Mods/ModMuted.cs | 3 +-- osu.Game/Rulesets/UI/DrawableRuleset.cs | 11 ++++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) rename osu.Game/Extensions/{EditorDisplayExtensions.cs => TimeDisplayExtensions.cs} (77%) diff --git a/osu.Game/Extensions/EditorDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs similarity index 77% rename from osu.Game/Extensions/EditorDisplayExtensions.cs rename to osu.Game/Extensions/TimeDisplayExtensions.cs index f749b88b46..dacde44ca1 100644 --- a/osu.Game/Extensions/EditorDisplayExtensions.cs +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -22,5 +22,11 @@ namespace osu.Game.Extensions /// An editor formatted display string. public static string ToEditorFormattedString(this TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}"; + + public static string ToFormattedDuration(this double milliseconds) => + ToFormattedDuration(TimeSpan.FromMilliseconds(milliseconds)); + + public static string ToFormattedDuration(this TimeSpan timeSpan) => + timeSpan.Hours == 0 ? $"{timeSpan:mm\\:ss}" : $"{timeSpan:HH\\:mm\\:ss}"; } } diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 9eb218fd9b..7fde14d6ca 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -7,7 +7,6 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Rulesets.Objects; @@ -82,7 +81,7 @@ namespace osu.Game.Rulesets.Mods } if (AffectsHitSounds.Value) - drawableRuleset.AudioContainer.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); + drawableRuleset.Audio.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index b3242a8b4b..29559f5036 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -99,12 +100,12 @@ namespace osu.Game.Rulesets.UI private DrawableRulesetDependencies dependencies; /// - /// An audio container which can be used to apply adjustments to playfield content. + /// Audio adjustments which are applied to the playfield. /// /// /// Does not affect . /// - public AudioContainer AudioContainer { get; private set; } + public IAdjustableAudioComponent Audio { get; private set; } /// /// Creates a ruleset visualisation for the provided ruleset and beatmap. @@ -163,13 +164,15 @@ namespace osu.Game.Rulesets.UI [BackgroundDependencyLoader] private void load(CancellationToken? cancellationToken) { + AudioContainer audioContainer; + InternalChild = frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) { FrameStablePlayback = FrameStablePlayback, Children = new Drawable[] { FrameStableComponents, - AudioContainer = new AudioContainer + audioContainer = new AudioContainer { RelativeSizeAxes = Axes.Both, Child = KeyBindingInputManager @@ -181,6 +184,8 @@ namespace osu.Game.Rulesets.UI } }; + Audio = audioContainer; + if ((ResumeOverlay = CreateResumeOverlay()) != null) { AddInternal(CreateInputManager() From f3d4f47e62551fe313070879a11dcca10a98ba98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 15:52:36 +0900 Subject: [PATCH 459/489] Revert unrelated changes --- ...{TimeDisplayExtensions.cs => EditorDisplayExtensions.cs} | 6 ------ 1 file changed, 6 deletions(-) rename osu.Game/Extensions/{TimeDisplayExtensions.cs => EditorDisplayExtensions.cs} (77%) diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/EditorDisplayExtensions.cs similarity index 77% rename from osu.Game/Extensions/TimeDisplayExtensions.cs rename to osu.Game/Extensions/EditorDisplayExtensions.cs index dacde44ca1..f749b88b46 100644 --- a/osu.Game/Extensions/TimeDisplayExtensions.cs +++ b/osu.Game/Extensions/EditorDisplayExtensions.cs @@ -22,11 +22,5 @@ namespace osu.Game.Extensions /// An editor formatted display string. public static string ToEditorFormattedString(this TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}"; - - public static string ToFormattedDuration(this double milliseconds) => - ToFormattedDuration(TimeSpan.FromMilliseconds(milliseconds)); - - public static string ToFormattedDuration(this TimeSpan timeSpan) => - timeSpan.Hours == 0 ? $"{timeSpan:mm\\:ss}" : $"{timeSpan:HH\\:mm\\:ss}"; } } From 472c0137ec856381ff6f5802007d268aad662a8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 16:45:53 +0900 Subject: [PATCH 460/489] Add new extension for formatting time durations --- .../NonVisual/TimeDisplayExtensionTest.cs | 41 +++++++++++++++ .../Extensions/EditorDisplayExtensions.cs | 26 ---------- osu.Game/Extensions/TimeDisplayExtensions.cs | 51 +++++++++++++++++++ 3 files changed, 92 insertions(+), 26 deletions(-) create mode 100644 osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs delete mode 100644 osu.Game/Extensions/EditorDisplayExtensions.cs create mode 100644 osu.Game/Extensions/TimeDisplayExtensions.cs diff --git a/osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs b/osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs new file mode 100644 index 0000000000..97d7880def --- /dev/null +++ b/osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs @@ -0,0 +1,41 @@ +// 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.Game.Extensions; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class TimeDisplayExtensionTest + { + private static readonly object[][] editor_formatted_duration_tests = + { + new object[] { new TimeSpan(0, 0, 0, 0, 50), "00:00:050" }, + new object[] { new TimeSpan(0, 0, 0, 10, 50), "00:10:050" }, + new object[] { new TimeSpan(0, 0, 5, 10), "05:10:000" }, + new object[] { new TimeSpan(0, 1, 5, 10), "65:10:000" }, + }; + + [TestCaseSource(nameof(editor_formatted_duration_tests))] + public void TestEditorFormat(TimeSpan input, string expectedOutput) + { + Assert.AreEqual(expectedOutput, input.ToEditorFormattedString()); + } + + private static readonly object[][] formatted_duration_tests = + { + new object[] { new TimeSpan(0, 0, 10), "00:10" }, + new object[] { new TimeSpan(0, 5, 10), "05:10" }, + new object[] { new TimeSpan(1, 5, 10), "01:05:10" }, + new object[] { new TimeSpan(1, 1, 5, 10), "01:01:05:10" }, + }; + + [TestCaseSource(nameof(formatted_duration_tests))] + public void TestFormattedDuration(TimeSpan input, string expectedOutput) + { + Assert.AreEqual(expectedOutput, input.ToFormattedDuration().ToString()); + } + } +} diff --git a/osu.Game/Extensions/EditorDisplayExtensions.cs b/osu.Game/Extensions/EditorDisplayExtensions.cs deleted file mode 100644 index f749b88b46..0000000000 --- a/osu.Game/Extensions/EditorDisplayExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Extensions -{ - public static class EditorDisplayExtensions - { - /// - /// Get an editor formatted string (mm:ss:mss) - /// - /// A time value in milliseconds. - /// An editor formatted display string. - public static string ToEditorFormattedString(this double milliseconds) => - ToEditorFormattedString(TimeSpan.FromMilliseconds(milliseconds)); - - /// - /// Get an editor formatted string (mm:ss:mss) - /// - /// A time value. - /// An editor formatted display string. - public static string ToEditorFormattedString(this TimeSpan timeSpan) => - $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}"; - } -} diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs new file mode 100644 index 0000000000..821686a349 --- /dev/null +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -0,0 +1,51 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Localisation; + +namespace osu.Game.Extensions +{ + public static class TimeDisplayExtensions + { + /// + /// Get an editor formatted string (mm:ss:mss) + /// + /// A time value in milliseconds. + /// An editor formatted display string. + public static string ToEditorFormattedString(this double milliseconds) => + ToEditorFormattedString(TimeSpan.FromMilliseconds(milliseconds)); + + /// + /// Get an editor formatted string (mm:ss:mss) + /// + /// A time value. + /// An editor formatted display string. + public static string ToEditorFormattedString(this TimeSpan timeSpan) => + $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{(int)timeSpan.TotalMinutes:00}:{timeSpan:ss\\:fff}"; + + /// + /// Get a formatted duration (mm:ss or HH:mm:ss if more than an hour). + /// + /// A duration in milliseconds. + /// A formatted duration string. + public static LocalisableString ToFormattedDuration(this double milliseconds) => + ToFormattedDuration(TimeSpan.FromMilliseconds(milliseconds)); + + /// + /// Get a formatted duration (dd:hh:mm:ss with days/hours omitted if zero). + /// + /// A duration value. + /// A formatted duration string. + public static LocalisableString ToFormattedDuration(this TimeSpan timeSpan) + { + if (timeSpan.TotalDays >= 1) + return new LocalisableFormattableString(timeSpan, @"dd\:hh\:mm\:ss"); + + if (timeSpan.TotalHours >= 1) + return new LocalisableFormattableString(timeSpan, @"hh\:mm\:ss"); + + return new LocalisableFormattableString(timeSpan, @"mm\:ss"); + } + } +} From 081dafc4e4682b8595808696acdaace63f5ce060 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 16:46:02 +0900 Subject: [PATCH 461/489] Update existing inline usages to use new extension method --- osu.Game.Tournament/Components/SongBar.cs | 6 +++--- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 3 ++- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index cafec0a88b..6080f7b636 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -11,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Rulesets; using osu.Game.Screens.Menu; @@ -198,8 +198,8 @@ namespace osu.Game.Tournament.Components Direction = FillDirection.Vertical, Children = new Drawable[] { - new DiffPiece(("Length", TimeSpan.FromMilliseconds(length).ToString(@"mm\:ss"))), - new DiffPiece(("BPM", $"{bpm:0.#}")) + new DiffPiece(("Length", length.ToFormattedDuration().ToString())), + new DiffPiece(("BPM", $"{bpm:0.#}")), } }, new Container diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index b81c60a5b9..3f1034759e 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -62,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet } else { - length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToString(@"m\:ss"); + length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration(); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 4a35202df2..763c27bcbb 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -24,6 +24,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -333,7 +334,7 @@ namespace osu.Game.Screens.Select { Name = "Length", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), - Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"), + Content = beatmap.BeatmapInfo.Length.ToFormattedDuration().ToString(), }), bpmLabelContainer = new Container { From 7a44ddb36baeafdab1fae0637571fab793ad145a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 16:48:43 +0900 Subject: [PATCH 462/489] Update incorrect xmldoc --- osu.Game/Extensions/TimeDisplayExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs index 821686a349..dc05482a05 100644 --- a/osu.Game/Extensions/TimeDisplayExtensions.cs +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -25,7 +25,7 @@ namespace osu.Game.Extensions $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{(int)timeSpan.TotalMinutes:00}:{timeSpan:ss\\:fff}"; /// - /// Get a formatted duration (mm:ss or HH:mm:ss if more than an hour). + /// Get a formatted duration (dd:hh:mm:ss with days/hours omitted if zero). /// /// A duration in milliseconds. /// A formatted duration string. From 73393a5a0d102795f1f895e7f49b6ae2cda609e9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 31 Jul 2021 15:56:25 +0200 Subject: [PATCH 463/489] Localise weighting percentage. --- .../Profile/Sections/Ranks/DrawableProfileWeightedScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs index 63305d004c..4e4a665a60 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -52,7 +53,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks new OsuSpriteText { Font = OsuFont.GetFont(size: 12), - Text = UsersStrings.ShowExtraTopRanksPpWeight(weight.ToString("0%")) + Text = UsersStrings.ShowExtraTopRanksPpWeight(weight.ToLocalisableString("0%")) } } }; From 9a7537cd569c9401efbcda2eca04b4dcbca166a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 19:48:21 +0200 Subject: [PATCH 464/489] Add support for adding new colours to palette --- .../Graphics/UserInterfaceV2/ColourPalette.cs | 131 ++++++++++++------ 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index d8edd00c16..1956eb045b 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -1,12 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; @@ -36,36 +41,24 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - private FillFlowContainer palette; - private Container placeholder; + private FillFlowContainer palette; + + private IEnumerable colourDisplays => palette.OfType(); [BackgroundDependencyLoader] private void load() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + AutoSizeDuration = fade_duration; + AutoSizeEasing = Easing.OutQuint; - InternalChildren = new Drawable[] + InternalChild = palette = new FillFlowContainer { - palette = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10), - Direction = FillDirection.Full - }, - placeholder = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Text = "(none)", - Font = OsuFont.Default.With(weight: FontWeight.Bold) - } - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Full }; } @@ -73,30 +66,20 @@ namespace osu.Game.Graphics.UserInterfaceV2 { base.LoadComplete(); - Colours.BindCollectionChanged((_, args) => updatePalette(args), true); + Colours.BindCollectionChanged((_, args) => + { + if (args.Action != NotifyCollectionChangedAction.Replace) + updatePalette(); + }, true); FinishTransforms(true); } private const int fade_duration = 200; - private void updatePalette(NotifyCollectionChangedEventArgs args) + private void updatePalette() { - if (args.Action == NotifyCollectionChangedAction.Replace) - return; - palette.Clear(); - if (Colours.Any()) - { - palette.FadeIn(fade_duration, Easing.OutQuint); - placeholder.FadeOut(fade_duration, Easing.OutQuint); - } - else - { - palette.FadeOut(fade_duration, Easing.OutQuint); - placeholder.FadeIn(fade_duration, Easing.OutQuint); - } - for (int i = 0; i < Colours.Count; ++i) { // copy to avoid accesses to modified closure. @@ -111,6 +94,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); } + palette.Add(new AddColourButton + { + Action = () => Colours.Add(Colour4.White) + }); + reindexItems(); } @@ -118,11 +106,74 @@ namespace osu.Game.Graphics.UserInterfaceV2 { int index = 1; - foreach (var colour in palette) + foreach (var colourDisplay in colourDisplays) { - colour.ColourName = $"{colourNamePrefix} {index}"; + colourDisplay.ColourName = $"{colourNamePrefix} {index}"; index += 1; } } + + private class AddColourButton : CompositeDrawable + { + public Action Action + { + set => circularButton.Action = value; + } + + private readonly OsuClickableContainer circularButton; + + public AddColourButton() + { + AutoSizeAxes = Axes.Y; + Width = 100; + + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + circularButton = new OsuClickableContainer + { + RelativeSizeAxes = Axes.X, + Height = 100, + CornerRadius = 50, + Masking = true, + BorderThickness = 5, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Transparent, + AlwaysPresent = true + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + Icon = FontAwesome.Solid.Plus + } + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "New" + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + circularButton.BorderColour = colours.BlueDarker; + } + } } } From 3f005886d66e1c4c19e7fd60e5589861ec616cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 20:02:39 +0200 Subject: [PATCH 465/489] Add support for removing colours from palette --- .../TestSceneLabelledColourPalette.cs | 19 ++-- .../Graphics/UserInterfaceV2/ColourDisplay.cs | 103 +++++++++++------- .../Graphics/UserInterfaceV2/ColourPalette.cs | 3 + 3 files changed, 81 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index d7af47a835..bbb5ebd0d9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterfaceV2; using osuTK.Graphics; @@ -34,17 +35,21 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create component", () => { - Child = new Container + Child = new OsuContextMenuContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 500, - AutoSizeAxes = Axes.Y, - Child = component = new LabelledColourPalette + RelativeSizeAxes = Axes.Both, + Child = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - ColourNamePrefix = "My colour #" + Width = 500, + AutoSizeAxes = Axes.Y, + Child = component = new LabelledColourPalette + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ColourNamePrefix = "My colour #" + } } }; diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 25f89fdcaa..85e18a6865 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -12,6 +13,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 @@ -19,12 +21,17 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// /// A component which displays a colour along with related description text. /// - public class ColourDisplay : CompositeDrawable, IHasCurrentValue, IHasPopover + public class ColourDisplay : CompositeDrawable, IHasCurrentValue { + /// + /// Invoked when the user has requested the colour corresponding to this + /// to be removed from its palette. + /// + public event Action DeleteRequested; + private readonly BindableWithCurrent current = new BindableWithCurrent(); - private Box fill; - private OsuSpriteText colourHexCode; + private OsuClickableContainer colourCircle; private OsuSpriteText colourName; public Bindable Current @@ -63,26 +70,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - new OsuClickableContainer + colourCircle = new ColourCircle { - RelativeSizeAxes = Axes.X, - Height = 100, - CornerRadius = 50, - Masking = true, - Children = new Drawable[] - { - fill = new Box - { - RelativeSizeAxes = Axes.Both - }, - colourHexCode = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.Default.With(size: 12) - } - }, - Action = this.ShowPopover + Current = { BindTarget = Current }, + DeleteRequested = () => DeleteRequested?.Invoke(this) }, colourName = new OsuSpriteText { @@ -93,26 +84,64 @@ namespace osu.Game.Graphics.UserInterfaceV2 }; } - protected override void LoadComplete() + private class ColourCircle : OsuClickableContainer, IHasPopover, IHasContextMenu { - base.LoadComplete(); + public Bindable Current { get; } = new Bindable(); - current.BindValueChanged(_ => updateColour(), true); - } + public Action DeleteRequested { get; set; } - private void updateColour() - { - fill.Colour = current.Value; - colourHexCode.Text = current.Value.ToHex(); - colourHexCode.Colour = OsuColour.ForegroundTextColourFor(current.Value); - } + private readonly Box fill; + private readonly OsuSpriteText colourHexCode; - public Popover GetPopover() => new OsuPopover(false) - { - Child = new OsuColourPicker + public ColourCircle() { - Current = { BindTarget = Current } + RelativeSizeAxes = Axes.X; + Height = 100; + CornerRadius = 50; + Masking = true; + Action = this.ShowPopover; + + Children = new Drawable[] + { + fill = new Box + { + RelativeSizeAxes = Axes.Both + }, + colourHexCode = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Default.With(size: 12) + } + }; } - }; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateColour(), true); + } + + private void updateColour() + { + fill.Colour = Current.Value; + colourHexCode.Text = Current.Value.ToHex(); + colourHexCode.Colour = OsuColour.ForegroundTextColourFor(Current.Value); + } + + public Popover GetPopover() => new OsuPopover(false) + { + Child = new OsuColourPicker + { + Current = { BindTarget = Current } + } + }; + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke()) + }; + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index 1956eb045b..02d03bf07e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -92,6 +92,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 }); display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); + display.DeleteRequested += colourDeletionRequested; } palette.Add(new AddColourButton @@ -102,6 +103,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 reindexItems(); } + private void colourDeletionRequested(ColourDisplay display) => Colours.RemoveAt(palette.IndexOf(display)); + private void reindexItems() { int index = 1; From 4334121e8ecff738618e04006e89f6dcd61ae549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 20:14:42 +0200 Subject: [PATCH 466/489] Add testing for colour palette behaviour --- .../TestSceneLabelledColourPalette.cs | 53 ++++++++++++++++++- .../Graphics/UserInterfaceV2/ColourPalette.cs | 2 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index bbb5ebd0d9..6fafb8f87a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -1,17 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneLabelledColourPalette : OsuTestScene + public class TestSceneLabelledColourPalette : OsuManualInputManagerTestScene { private LabelledColourPalette component; @@ -31,6 +35,22 @@ namespace osu.Game.Tests.Visual.UserInterface }, 8); } + [Test] + public void TestUserInteractions() + { + createColourPalette(); + assertColourCount(4); + + clickAddColour(); + assertColourCount(5); + + deleteFirstColour(); + assertColourCount(4); + + clickFirstColour(); + AddAssert("colour picker spawned", () => this.ChildrenOfType().Any()); + } + private void createColourPalette(bool hasDescription = false) { AddStep("create component", () => @@ -71,5 +91,36 @@ namespace osu.Game.Tests.Visual.UserInterface RNG.NextSingle(), RNG.NextSingle(), 1); + + private void assertColourCount(int count) => AddAssert($"colour count is {count}", () => component.Colours.Count == count); + + private void clickAddColour() => AddStep("click new colour button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + private void clickFirstColour() => AddStep("click first colour", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + + private void deleteFirstColour() + { + AddStep("right-click first colour", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Right); + }); + + AddUntilStep("wait for menu", () => this.ChildrenOfType().Any()); + + AddStep("click delete", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index 02d03bf07e..a966f61b74 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - private class AddColourButton : CompositeDrawable + internal class AddColourButton : CompositeDrawable { public Action Action { From 708b50fdba0016e950b1a4446e35534f50bf3720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 00:11:56 +0200 Subject: [PATCH 467/489] Remove unused field --- osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 85e18a6865..5240df74a2 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -31,7 +31,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 private readonly BindableWithCurrent current = new BindableWithCurrent(); - private OsuClickableContainer colourCircle; private OsuSpriteText colourName; public Bindable Current @@ -70,7 +69,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - colourCircle = new ColourCircle + new ColourCircle { Current = { BindTarget = Current }, DeleteRequested = () => DeleteRequested?.Invoke(this) From f868a201f5347c607c65cf62e614faa8c268d9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 15:03:51 +0200 Subject: [PATCH 468/489] Ensure proxied judgement content is correctly depth-ordered --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 6 +++++- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 8993a9b18a..c1c2ea2299 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.UI private void onJudgementLoaded(DrawableOsuJudgement judgement) { - judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent()); + judgementAboveHitObjectLayer.Add(judgement.ProxiedAboveHitObjectsContent); } [BackgroundDependencyLoader(true)] @@ -150,6 +150,10 @@ namespace osu.Game.Rulesets.Osu.UI DrawableOsuJudgement explosion = poolDictionary[result.Type].Get(doj => doj.Apply(result, judgedObject)); judgementLayer.Add(explosion); + + // the proxied content is added to judgementAboveHitObjectLayer once, on first load, and never removed from it. + // ensure that ordering is consistent with expectations (latest judgement should be front-most). + judgementAboveHitObjectLayer.ChangeChildDepth(explosion.ProxiedAboveHitObjectsContent, (float)-result.TimeAbsolute); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 0f22d35bb5..d25d46c6e2 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.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 System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -31,6 +32,9 @@ namespace osu.Game.Rulesets.Judgements private readonly Container aboveHitObjectsContent; + private readonly Lazy proxiedAboveHitObjectsContent; + public Drawable ProxiedAboveHitObjectsContent => proxiedAboveHitObjectsContent.Value; + /// /// Creates a drawable which visualises a . /// @@ -52,6 +56,8 @@ namespace osu.Game.Rulesets.Judgements Depth = float.MinValue, RelativeSizeAxes = Axes.Both }); + + proxiedAboveHitObjectsContent = new Lazy(() => aboveHitObjectsContent.CreateProxy()); } [BackgroundDependencyLoader] @@ -60,8 +66,6 @@ namespace osu.Game.Rulesets.Judgements prepareDrawables(); } - public Drawable GetProxyAboveHitObjectsContent() => aboveHitObjectsContent.CreateProxy(); - /// /// Apply top-level animations to the current judgement when successfully hit. /// If displaying components which require lifetime extensions, manually adjusting is required. From ac930b8918fcaa731965674cc6610fcb33f9a153 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 19:16:30 +0300 Subject: [PATCH 469/489] Fix judgement processors provided to mods while not completely loaded --- .../Rulesets/Mods/IApplicableToHealthProcessor.cs | 2 +- .../Rulesets/Mods/IApplicableToScoreProcessor.cs | 2 +- osu.Game/Screens/Play/Player.cs | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs index a181955653..c12b215d41 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToHealthProcessor : IApplicableMod { /// - /// Provide a to a mod. Called once on initialisation of a play instance. + /// Provides a ready to a mod. Called once on initialisation of a play instance. /// void ApplyToHealthProcessor(HealthProcessor healthProcessor); } diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs index cb00770868..b93e50921f 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToScoreProcessor : IApplicableMod { /// - /// Provide a to a mod. Called once on initialisation of a play instance. + /// Provides a loaded to a mod. Called once on initialisation of a play instance. /// void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d76d44767a..db6ec1f4a9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -297,11 +297,17 @@ namespace osu.Game.Screens.Play ScoreProcessor.HasCompleted.BindValueChanged(scoreCompletionChanged); HealthProcessor.Failed += onFail; - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToScoreProcessor(ScoreProcessor); + ScoreProcessor.OnLoadComplete += _ => + { + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToScoreProcessor(ScoreProcessor); + }; - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToHealthProcessor(HealthProcessor); + HealthProcessor.OnLoadComplete += _ => + { + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToHealthProcessor(HealthProcessor); + }; IsBreakTime.BindTo(breakTracker.IsBreakTime); IsBreakTime.BindValueChanged(onBreakTimeChanged, true); From f12e66052c99581040e8d25822e9a979e041bf84 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 19:22:33 +0300 Subject: [PATCH 470/489] Reword outdated doc --- osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs index c12b215d41..2676060efa 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToHealthProcessor : IApplicableMod { /// - /// Provides a ready to a mod. Called once on initialisation of a play instance. + /// Provides a loaded to a mod. Called once on initialisation of a play instance. /// void ApplyToHealthProcessor(HealthProcessor healthProcessor); } From c93533fa2e14407baec6c761bf2bf4ca06eb641b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:04:24 +0000 Subject: [PATCH 471/489] Bump Xamarin.Essentials from 1.6.1 to 1.7.0 Bumps [Xamarin.Essentials](https://github.com/xamarin/Essentials) from 1.6.1 to 1.7.0. - [Release notes](https://github.com/xamarin/Essentials/releases) - [Commits](https://github.com/xamarin/Essentials/compare/1.6.1...1.7.0) --- updated-dependencies: - dependency-name: Xamarin.Essentials dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Android/osu.Android.csproj | 2 +- osu.iOS/osu.iOS.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 582c856a47..b2599535ae 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -64,7 +64,7 @@ - + \ No newline at end of file diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 1cbe4422cc..1203c3659b 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -117,7 +117,7 @@ - + From 81f42da38678c1f3b6ebb8980f8d6ad4c759afb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:04:30 +0000 Subject: [PATCH 472/489] Bump MessagePack from 2.2.113 to 2.3.75 Bumps [MessagePack](https://github.com/neuecc/MessagePack-CSharp) from 2.2.113 to 2.3.75. - [Release notes](https://github.com/neuecc/MessagePack-CSharp/releases) - [Changelog](https://github.com/neuecc/MessagePack-CSharp/blob/master/prepare_release.ps1) - [Commits](https://github.com/neuecc/MessagePack-CSharp/compare/v2.2.113...v2.3.75) --- updated-dependencies: - dependency-name: MessagePack dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- 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 936fe8db94..7ca52aaed5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + From aadd1c67816b5affa75b6df49b4a955db4ee2a40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:04:37 +0000 Subject: [PATCH 473/489] Bump Sentry from 3.8.2 to 3.8.3 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.8.2 to 3.8.3. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.8.2...3.8.3) --- updated-dependencies: - dependency-name: Sentry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 936fe8db94..03ae29a384 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -38,7 +38,7 @@ - + From a75da8298686c180fe657a75899fc9838866251c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 21:14:54 +0300 Subject: [PATCH 474/489] Add explaining comment --- osu.Game/Screens/Play/Player.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index db6ec1f4a9..09eaf1c543 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -297,6 +297,8 @@ namespace osu.Game.Screens.Play ScoreProcessor.HasCompleted.BindValueChanged(scoreCompletionChanged); HealthProcessor.Failed += onFail; + // Provide judgement processors to mods after they're loaded so that they're on the gameplay clock, + // this is required for mods that apply transforms to these processors. ScoreProcessor.OnLoadComplete += _ => { foreach (var mod in Mods.Value.OfType()) From df9b6182560909c892ffd8ed7a324facb2504ec4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 11:44:05 +0900 Subject: [PATCH 475/489] Add localisation license header to editorconfig --- osu.Game/.editorconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/.editorconfig b/osu.Game/.editorconfig index 46a3dafd04..4107d1bb35 100644 --- a/osu.Game/.editorconfig +++ b/osu.Game/.editorconfig @@ -1,2 +1,3 @@ [*.cs] -dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation \ No newline at end of file +dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation +dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. \ No newline at end of file From 356c157d1f860e621c5fdc594b4c95050facf158 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 11:49:21 +0900 Subject: [PATCH 476/489] Add localisation options to sln's .editorconfig --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 19bd89c52f..ceaaf4e8f1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -190,3 +190,6 @@ dotnet_diagnostic.CA2225.severity = none # Banned APIs dotnet_diagnostic.RS0030.severity = error + +dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation +dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From cd0fb2f4360cc4e3aae2003e9319989f88e27765 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 11:52:10 +0900 Subject: [PATCH 477/489] Remove namespace from sln's .editorconfig --- .editorconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index ceaaf4e8f1..3c4997c88d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -191,5 +191,4 @@ dotnet_diagnostic.CA2225.severity = none # Banned APIs dotnet_diagnostic.RS0030.severity = error -dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From 3e56388ba8ea4217f197cbb15ef78f216cbfb799 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 15:08:42 +0900 Subject: [PATCH 478/489] Match casing in enum value --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9422d6d51b..b30c3d82a4 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.UI } if (!lastHyperDashState && Catcher.HyperDashing) - displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterimage); + displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterImage); if (Catcher.Dashing || Catcher.HyperDashing) { diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index f3f996d2c5..6d2ac7e488 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.UI this.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); break; - case CatcherTrailAnimation.HyperDashAfterimage: + case CatcherTrailAnimation.HyperDashAfterImage: this.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); this.ScaleTo(Scale * 0.95f).ScaleTo(Scale * 1.2f, 1200, Easing.In); this.FadeOut(1200); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs index 3ea99badc1..0a5281cd10 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.UI { Dashing, HyperDashing, - HyperDashAfterimage + HyperDashAfterImage } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index f451f84c95..0f2530e56a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashTrails.Add(drawable); break; - case CatcherTrailAnimation.HyperDashAfterimage: + case CatcherTrailAnimation.HyperDashAfterImage: hyperDashAfterImages.Add(drawable); break; } @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashTrails.Remove(drawable); break; - case CatcherTrailAnimation.HyperDashAfterimage: + case CatcherTrailAnimation.HyperDashAfterImage: hyperDashAfterImages.Remove(drawable); break; } From c023ce78d79a1dad4da6fc6138cdb02940237691 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 18:48:32 +0900 Subject: [PATCH 479/489] Match osu!stable taiko playfield size at 16:9 --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 46dafc3a30..0d9e08b8b7 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.UI /// /// Default height of a when inside a . /// - public const float DEFAULT_HEIGHT = 178; + public const float DEFAULT_HEIGHT = 212; private Container hitExplosionContainer; private Container kiaiExplosionContainer; From 2af827f9133336fa39772758dff92d9349914b8b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 19:37:45 +0900 Subject: [PATCH 480/489] Increase TimeRange max value --- osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 6ffdad211b..f8d5a6c5a9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// /// The maximum span of time that may be visible by the length of the scrolling axes. /// - private const double time_span_max = 10000; + private const double time_span_max = 20000; /// /// The step increase/decrease of the span of time visible by the length of the scrolling axes. From 9327bc169b9449828373786f2a00afbc069a2c31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 19:39:30 +0900 Subject: [PATCH 481/489] Make TaikoModClassic use classic-like scroll speed --- .../Mods/TaikoModClassic.cs | 22 ++++++++++++++++++- .../UI/DrawableTaikoRuleset.cs | 5 ++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 5a4d18be98..6520517039 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -2,10 +2,30 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModClassic : ModClassic + public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset, IUpdatableByPlayfield { + private DrawableTaikoRuleset drawableTaikoRuleset; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + } + + public void Update(Playfield playfield) + { + // Classic taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened. + const float scroll_rate = 10; + + // Since the time range will depend on a positional value, it is referenced to the x480 pixel space. + float ratio = drawableTaikoRuleset.DrawHeight / 480; + + drawableTaikoRuleset.TimeRange.Value = (playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate; + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index ed8e6859a2..650ce1f5a3 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; @@ -24,12 +25,14 @@ namespace osu.Game.Rulesets.Taiko.UI { public class DrawableTaikoRuleset : DrawableScrollingRuleset { - private SkinnableDrawable scroller; + public new BindableDouble TimeRange => base.TimeRange; protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; protected override bool UserScrollSpeedAdjustment => false; + private SkinnableDrawable scroller; + public DrawableTaikoRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { From 455666ed94af154106caad62e3a5a5fe0316ace9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 20:18:01 +0900 Subject: [PATCH 482/489] Remove taiko HD mod 4:3 scaling --- .../Mods/TaikoModHidden.cs | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 0fd3625a93..8406ab8505 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -17,18 +17,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; - /// - /// In osu-stable, the hit position is 160, so the active playfield is essentially 160 pixels shorter - /// than the actual screen width. The normalized playfield height is 480, so on a 4:3 screen the - /// playfield ratio of the active area up to the hit position will actually be (640 - 160) / 480 = 1. - /// For custom resolutions/aspect ratios (x:y), the screen width given the normalized height becomes 480 * x / y instead, - /// and the playfield ratio becomes (480 * x / y - 160) / 480 = x / y - 1/3. - /// This constant is equal to the playfield ratio on 4:3 screens divided by the playfield ratio on 16:9 screens. - /// - private const double hd_sv_scale = (4.0 / 3.0 - 1.0 / 3.0) / (16.0 / 9.0 - 1.0 / 3.0); - - private double originalSliderMultiplier; - private ControlPointInfo controlPointInfo; protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) @@ -41,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Mods double beatLength = controlPointInfo.TimingPointAt(position).BeatLength; double speedMultiplier = controlPointInfo.DifficultyPointAt(position).SpeedMultiplier; - return originalSliderMultiplier * speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength; + return speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength; } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) @@ -69,22 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Mods } } - public void ReadFromDifficulty(BeatmapDifficulty difficulty) - { - } - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - // needs to be read after all processing has been run (TaikoBeatmapConverter applies an adjustment which would otherwise be omitted). - originalSliderMultiplier = difficulty.SliderMultiplier; - - // osu-stable has an added playfield cover that essentially forces a 4:3 playfield ratio, by cutting off all objects past that size. - // This is not yet implemented; instead a playfield adjustment container is present which maintains a 16:9 ratio. - // For now, increase the slider multiplier proportionally so that the notes stay on the screen for the same amount of time as on stable. - // Note that this means that the notes will scroll faster as they have a longer distance to travel on the screen in that same amount of time. - difficulty.SliderMultiplier /= hd_sv_scale; - } - public override void ApplyToBeatmap(IBeatmap beatmap) { controlPointInfo = beatmap.ControlPointInfo; From 8d999d30f6de986ac8758a9983a6c400606fb3b6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 20:38:46 +0900 Subject: [PATCH 483/489] Remove interface definition --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 8406ab8505..a6b3fe1cd9 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModHidden : ModHidden, IApplicableToDifficulty + public class TaikoModHidden : ModHidden { public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; From 8d1586261d47f3738e91adc37dc287746edf4887 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 15:59:13 +0900 Subject: [PATCH 484/489] Update resources --- 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 c1075cfb17..b01210a7b1 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7e32f1e9fd..b3eeaa4be7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7b7d5f80fe..7ad27b222c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 404faabbbc5d77f5497628fa5968ebb0081131e2 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Tue, 3 Aug 2021 15:34:21 +0800 Subject: [PATCH 485/489] Use direct reference instead --- .../NamedOverlayComponentStrings.cs | 22 ------------------- .../BeatmapListing/BeatmapListingHeader.cs | 3 ++- .../Overlays/BeatmapSet/BeatmapSetHeader.cs | 4 ++-- .../Overlays/Changelog/ChangelogHeader.cs | 2 +- .../Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- .../Rankings/RankingsOverlayHeader.cs | 3 ++- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- 8 files changed, 10 insertions(+), 30 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index f160ee57c7..475bea2a4a 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; -using osu.Game.Resources.Localisation.Web; namespace osu.Game.Localisation { @@ -10,52 +9,31 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.NamedOverlayComponent"; - /// - public static LocalisableString BeatmapListingTitle => PageTitleStrings.MainBeatmapsetsControllerIndex; - /// /// "browse for new beatmaps" /// public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing_description"), @"browse for new beatmaps"); - /// - public static LocalisableString BeatmapSetTitle => PageTitleStrings.MainBeatmapsetsControllerShow; - - /// - public static LocalisableString ChangelogTitle => PageTitleStrings.MainChangelogControllerDefault; - /// /// "track recent dev updates in the osu! ecosystem" /// public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog_description"), @"track recent dev updates in the osu! ecosystem"); - /// - public static LocalisableString DashboardTitle => PageTitleStrings.MainHomeControllerIndex; - /// /// "view your friends and other information" /// public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard_description"), @"view your friends and other information"); - /// - public static LocalisableString RankingsTitle => PageTitleStrings.MainRankingControllerDefault; - /// /// "find out who's the best right now" /// public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings_description"), @"find out who's the best right now"); - /// - public static LocalisableString NewsTitle => PageTitleStrings.MainNewsControllerDefault; - /// /// "get up-to-date on community happenings" /// public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news_description"), @"get up-to-date on community happenings"); - /// - public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; - /// /// "knowledge base" /// diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index fa2ee0d735..3568fe9e4f 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.BeatmapListing { public BeatmapListingTitle() { - Title = NamedOverlayComponentStrings.BeatmapListingTitle; + Title = PageTitleStrings.MainBeatmapsetsControllerIndex; Description = NamedOverlayComponentStrings.BeatmapListingDescription; IconTexture = "Icons/Hexacons/beatmap"; } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 4fd24d9819..4a0c0e9f75 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -7,7 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; using osu.Game.Beatmaps; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet { public BeatmapHeaderTitle() { - Title = NamedOverlayComponentStrings.BeatmapSetTitle; + Title = PageTitleStrings.MainBeatmapsetsControllerShow; IconTexture = "Icons/Hexacons/beatmap"; } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 8707883ddd..52dea63ab7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Changelog { public ChangelogHeaderTitle() { - Title = NamedOverlayComponentStrings.ChangelogTitle; + Title = PageTitleStrings.MainChangelogControllerDefault; Description = NamedOverlayComponentStrings.ChangelogDescription; IconTexture = "Icons/Hexacons/devtools"; } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 05c9f30ff3..70991d47b7 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Dashboard { public DashboardTitle() { - Title = NamedOverlayComponentStrings.DashboardTitle; + Title = PageTitleStrings.MainHomeControllerIndex; Description = NamedOverlayComponentStrings.DashboardDescription; IconTexture = "Icons/Hexacons/social"; } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index bf739e1e8e..35e3c7755d 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = NamedOverlayComponentStrings.NewsTitle; + Title = PageTitleStrings.MainNewsControllerDefault; Description = NamedOverlayComponentStrings.NewsDescription; IconTexture = "Icons/Hexacons/news"; } diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index c0f77049de..4916b8b327 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Users; @@ -30,7 +31,7 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = NamedOverlayComponentStrings.RankingsTitle; + Title = PageTitleStrings.MainRankingControllerDefault; Description = NamedOverlayComponentStrings.RankingsDescription; IconTexture = "Icons/Hexacons/rankings"; } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index b57bffb3d8..3e81d2cffe 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = NamedOverlayComponentStrings.WikiTitle; + Title = PageTitleStrings.MainWikiControllerDefault; Description = NamedOverlayComponentStrings.WikiDescription; IconTexture = "Icons/Hexacons/wiki"; } From 062207fcd97a079fd88919758f12c5e45835657b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Aug 2021 18:16:58 +0900 Subject: [PATCH 486/489] Fix TestSceneCurrentlyPlayingDisplay reusing cached spectator client --- .../TestSceneCurrentlyPlayingDisplay.cs | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 30785fd163..2f11fec6d1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -1,13 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Online.Spectator; @@ -21,51 +20,44 @@ namespace osu.Game.Tests.Visual.Online { private readonly User streamingUser = new User { Id = 2, Username = "Test user" }; - [Cached(typeof(SpectatorClient))] - private TestSpectatorClient testSpectatorClient = new TestSpectatorClient(); - + private TestSpectatorClient spectatorClient; private CurrentlyPlayingDisplay currentlyPlaying; - [Cached(typeof(UserLookupCache))] - private UserLookupCache lookupCache = new TestUserLookupCache(); - - private Container nestedContainer; - [SetUpSteps] public void SetUpSteps() { AddStep("add streaming client", () => { - nestedContainer?.Remove(testSpectatorClient); - Remove(lookupCache); + spectatorClient = new TestSpectatorClient(); + var lookupCache = new TestUserLookupCache(); Children = new Drawable[] { lookupCache, - nestedContainer = new Container + spectatorClient, + new DependencyProvidingContainer { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + CachedDependencies = new (Type, object)[] { - testSpectatorClient, - currentlyPlaying = new CurrentlyPlayingDisplay - { - RelativeSizeAxes = Axes.Both, - } + (typeof(SpectatorClient), spectatorClient), + (typeof(UserLookupCache), lookupCache) + }, + Child = currentlyPlaying = new CurrentlyPlayingDisplay + { + RelativeSizeAxes = Axes.Both, } }, }; }); - - AddStep("Reset players", () => testSpectatorClient.EndPlay(streamingUser.Id)); } [Test] public void TestBasicDisplay() { - AddStep("Add playing user", () => testSpectatorClient.StartPlay(streamingUser.Id, 0)); + AddStep("Add playing user", () => spectatorClient.StartPlay(streamingUser.Id, 0)); AddUntilStep("Panel loaded", () => currentlyPlaying.ChildrenOfType()?.FirstOrDefault()?.User.Id == 2); - AddStep("Remove playing user", () => testSpectatorClient.EndPlay(streamingUser.Id)); + AddStep("Remove playing user", () => spectatorClient.EndPlay(streamingUser.Id)); AddUntilStep("Panel no longer present", () => !currentlyPlaying.ChildrenOfType().Any()); } From 11b9ba86cbed430a2cb0c02dcb0087d168aa81e4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Aug 2021 18:28:08 +0900 Subject: [PATCH 487/489] Fix TestSceneSpectator reusing cached spectator client --- .../Visual/Gameplay/TestSceneSpectator.cs | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 7584d67afe..21c5d89aca 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -13,6 +13,7 @@ using osu.Game.Online.Spectator; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Visual.Multiplayer; @@ -25,41 +26,43 @@ namespace osu.Game.Tests.Visual.Gameplay { private readonly User streamingUser = new User { Id = MultiplayerTestScene.PLAYER_1_ID, Username = "Test user" }; - [Cached(typeof(SpectatorClient))] - private TestSpectatorClient testSpectatorClient = new TestSpectatorClient(); - [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); // used just to show beatmap card for the time being. protected override bool UseOnlineAPI => true; - private SoloSpectator spectatorScreen; - [Resolved] private OsuGameBase game { get; set; } - private BeatmapSetInfo importedBeatmap; + private TestSpectatorClient spectatorClient; + private SoloSpectator spectatorScreen; + private BeatmapSetInfo importedBeatmap; private int importedBeatmapId; - public override void SetUpSteps() + [SetUpSteps] + public void SetupSteps() { - base.SetUpSteps(); + DependenciesScreen dependenciesScreen = null; + + AddStep("load dependencies", () => + { + spectatorClient = new TestSpectatorClient(); + + // The screen gets suspended so it stops receiving updates. + Child = spectatorClient; + + LoadScreen(dependenciesScreen = new DependenciesScreen(spectatorClient)); + }); + + AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); AddStep("import beatmap", () => { importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result; importedBeatmapId = importedBeatmap.Beatmaps.First(b => b.RulesetID == 0).OnlineBeatmapID ?? -1; }); - - AddStep("add streaming client", () => - { - Remove(testSpectatorClient); - Add(testSpectatorClient); - }); - - finish(); } [Test] @@ -206,22 +209,36 @@ namespace osu.Game.Tests.Visual.Gameplay private void waitForPlayer() => AddUntilStep("wait for player", () => (Stack.CurrentScreen as Player)?.IsLoaded == true); - private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); + private void start(int? beatmapId = null) => AddStep("start play", () => spectatorClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); - private void finish() => AddStep("end play", () => testSpectatorClient.EndPlay(streamingUser.Id)); + private void finish() => AddStep("end play", () => spectatorClient.EndPlay(streamingUser.Id)); private void checkPaused(bool state) => AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType().First().IsPaused.Value == state); private void sendFrames(int count = 10) { - AddStep("send frames", () => testSpectatorClient.SendFrames(streamingUser.Id, count)); + AddStep("send frames", () => spectatorClient.SendFrames(streamingUser.Id, count)); } private void loadSpectatingScreen() { - AddStep("load screen", () => LoadScreen(spectatorScreen = new SoloSpectator(streamingUser))); + AddStep("load spectator", () => LoadScreen(spectatorScreen = new SoloSpectator(streamingUser))); AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded); } + + /// + /// Used for the sole purpose of adding as a resolvable dependency. + /// + private class DependenciesScreen : OsuScreen + { + [Cached(typeof(SpectatorClient))] + public readonly TestSpectatorClient Client; + + public DependenciesScreen(TestSpectatorClient client) + { + Client = client; + } + } } } From 6b2ea1b08f638cc953956ee776e8fd3bcd69f60b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 18:30:04 +0900 Subject: [PATCH 488/489] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b01210a7b1..1866acd248 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b3eeaa4be7..4b0edf990e 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 7ad27b222c..e4992e1132 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1152e15282b6497be0a6a904c61617f0f7e2efc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 18:29:02 +0900 Subject: [PATCH 489/489] Update new usages of `LocalisableEnum` --- .../Rankings/RankingsOverlayHeader.cs | 2 +- osu.Game/Overlays/Rankings/RankingsScope.cs | 33 ++++--------------- .../Rankings/RankingsSortTabControl.cs | 23 ++----------- 3 files changed, 11 insertions(+), 47 deletions(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 4916b8b327..417b33ddf6 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; +using osu.Framework.Graphics; using osu.Game.Rulesets; using osu.Game.Users; diff --git a/osu.Game/Overlays/Rankings/RankingsScope.cs b/osu.Game/Overlays/Rankings/RankingsScope.cs index 684408d3a2..e660c2898a 100644 --- a/osu.Game/Overlays/Rankings/RankingsScope.cs +++ b/osu.Game/Overlays/Rankings/RankingsScope.cs @@ -1,42 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Rankings { - [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] public enum RankingsScope { + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypePerformance))] Performance, + + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeCharts))] Spotlights, + + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeScore))] Score, + + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeCountry))] Country } - - public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(RankingsScope value) - { - switch (value) - { - case RankingsScope.Performance: - return RankingsStrings.TypePerformance; - - case RankingsScope.Spotlights: - return RankingsStrings.TypeCharts; - - case RankingsScope.Score: - return RankingsStrings.TypeScore; - - case RankingsScope.Country: - return RankingsStrings.TypeCountry; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs index c04eb5bdd1..f05795b2a2 100644 --- a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs +++ b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -16,28 +15,12 @@ namespace osu.Game.Overlays.Rankings } } - [LocalisableEnum(typeof(RankingsSortCriteriaEnumLocalisationMapper))] public enum RankingsSortCriteria { + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.All))] All, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Friends))] Friends } - - public class RankingsSortCriteriaEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(RankingsSortCriteria value) - { - switch (value) - { - case RankingsSortCriteria.All: - return SortStrings.All; - - case RankingsSortCriteria.Friends: - return SortStrings.Friends; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } }