From 12bb4b697c4c58e806cfdb4105940d10db52f657 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 22 Oct 2023 03:27:21 +0300 Subject: [PATCH 001/207] Support retrieving textures in argon skin from game resources --- osu.Game/Skinning/ArgonSkin.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index d530efbfdd..641715b2c7 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -8,6 +8,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.Extensions; @@ -40,7 +41,13 @@ namespace osu.Game.Skinning [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public ArgonSkin(SkinInfo skin, IStorageResourceProvider resources) - : base(skin, resources) + : base( + skin, + resources, + // In the case of the actual default legacy skin (ie. the fallback one, which a user hasn't applied any modifications to) we want to use the game provided resources. + // todo: I don't know if this is required. + skin.Protected ? new NamespacedResourceStore(resources.Resources, "Skins/Argon") : null + ) { Resources = resources; From 8ed660d863cc32fe754487ad98dd3140f75669aa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Oct 2023 01:58:25 +0300 Subject: [PATCH 002/207] Remove incorrect logic --- osu.Game/Skinning/ArgonSkin.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 641715b2c7..26f2edf1aa 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -44,9 +44,7 @@ namespace osu.Game.Skinning : base( skin, resources, - // In the case of the actual default legacy skin (ie. the fallback one, which a user hasn't applied any modifications to) we want to use the game provided resources. - // todo: I don't know if this is required. - skin.Protected ? new NamespacedResourceStore(resources.Resources, "Skins/Argon") : null + new NamespacedResourceStore(resources.Resources, "Skins/Argon") ) { Resources = resources; From 9ca672d689ecf08c5f7c8a08d5e46ada684cac36 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Oct 2023 01:58:41 +0300 Subject: [PATCH 003/207] Add "Argon" total score counter --- .../TestSceneSkinnableScoreCounter.cs | 1 + .../Screens/Play/HUD/ArgonScoreCounter.cs | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs index 7079b93d3e..186680a79c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs @@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(ScoreProcessor))] private ScoreProcessor scoreProcessor = TestGameplayState.Create(new OsuRuleset()).ScoreProcessor; + protected override Drawable CreateArgonImplementation() => new ArgonScoreCounter(); protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter(); protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter(); diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs new file mode 100644 index 0000000000..cfda34578b --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -0,0 +1,124 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Framework.Text; +using osu.Game.Configuration; +using osu.Game.Graphics.Sprites; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonScoreCounter : GameplayScoreCounter, ISerialisableDrawable + { + [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] + public BindableFloat WireframeOpactiy { get; } = new BindableFloat(0.4f) + { + Precision = 0.01f, + MinValue = 0, + MaxValue = 1, + }; + + public bool UsesFixedAnchor { get; set; } + + protected override IHasText CreateText() => new ArgonScoreTextComponent + { + WireframeOpactiy = { BindTarget = WireframeOpactiy }, + }; + + private partial class ArgonScoreTextComponent : CompositeDrawable, IHasText + { + private readonly ArgonScoreSpriteText wireframesPart; + private readonly ArgonScoreSpriteText textPart; + + public IBindable WireframeOpactiy { get; } = new BindableFloat(); + + public LocalisableString Text + { + get => textPart.Text; + set + { + wireframesPart.Text = value; + textPart.Text = value; + } + } + + public ArgonScoreTextComponent() + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new[] + { + wireframesPart = new ArgonScoreSpriteText(@"wireframes"), + textPart = new ArgonScoreSpriteText(), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + WireframeOpactiy.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true); + } + + private partial class ArgonScoreSpriteText : OsuSpriteText + { + private readonly string? glyphLookupOverride; + + private GlyphStore glyphStore = null!; + + protected override char FixedWidthReferenceCharacter => '5'; + + public ArgonScoreSpriteText(string? glyphLookupOverride = null) + { + this.glyphLookupOverride = glyphLookupOverride; + + Shadow = false; + UseFullGlyphHeight = false; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + Font = new FontUsage(@"argon-score", 1, fixedWidth: true); + Spacing = new Vector2(-2, 0); + + glyphStore = new GlyphStore(skin, glyphLookupOverride); + } + + protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); + + private class GlyphStore : ITexturedGlyphLookupStore + { + private readonly ISkin skin; + private readonly string? glyphLookupOverride; + + public GlyphStore(ISkin skin, string? glyphLookupOverride) + { + this.skin = skin; + this.glyphLookupOverride = glyphLookupOverride; + } + + public ITexturedCharacterGlyph? Get(string fontName, char character) + { + string lookup = glyphLookupOverride ?? character.ToString(); + var texture = skin.GetTexture($"{fontName}-{lookup}"); + + if (texture == null) + return null; + + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.25f); + } + + public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); + } + } + } + } +} From 1f36acca1a6c31f4ca73c487ac63795fd35ddacf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 08:58:47 +0300 Subject: [PATCH 004/207] Add "Argon" accuracy counter --- .../TestSceneSkinnableAccuracyCounter.cs | 1 + .../Screens/Play/HUD/ArgonAccuracyCounter.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs index 59a1f938e6..e088d2ca87 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs @@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset()); + protected override Drawable CreateArgonImplementation() => new ArgonAccuracyCounter(); protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter(); protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter(); diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs new file mode 100644 index 0000000000..33223a526b --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.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.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonAccuracyCounter : GameplayAccuracyCounter, ISerialisableDrawable + { + public bool UsesFixedAnchor { get; set; } + + protected override OsuSpriteText CreateSpriteText() + => base.CreateSpriteText().With(s => s.Font = OsuFont.Default.With(size: 19.2f)); + } +} From 0aa0562dc0a5b645332c309bac55e7a444eb5c56 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 08:59:04 +0300 Subject: [PATCH 005/207] Add "Argon" combo counter --- .../TestSceneSkinnableComboCounter.cs | 1 + .../Screens/Play/HUD/ArgonComboCounter.cs | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/ArgonComboCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs index 0c351a93bb..72f40d9c6f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs @@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset()); + protected override Drawable CreateArgonImplementation() => new ArgonComboCounter(); protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter(); protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter(); diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs new file mode 100644 index 0000000000..28c97c53aa --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonComboCounter : ComboCounter + { + [BackgroundDependencyLoader] + private void load(ScoreProcessor scoreProcessor) + { + Current.BindTo(scoreProcessor.Combo); + } + + protected override OsuSpriteText CreateSpriteText() + => base.CreateSpriteText().With(s => s.Font = FontUsage.Default.With(size: 36f)); + + protected override LocalisableString FormatCount(int count) + { + return $@"{count}x"; + } + } +} From daf4a03fd092ac84955a8f63fa3c45200a28cf04 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 08:59:47 +0300 Subject: [PATCH 006/207] Abstractify PP counter logic from the "Triangles" implementation --- .../HUD/GameplayPerformancePointsCounter.cs | 186 ++++++++++++++++++ .../Play/HUD/PerformancePointsCounter.cs | 177 +---------------- 2 files changed, 190 insertions(+), 173 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs diff --git a/osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs new file mode 100644 index 0000000000..a812a3e043 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs @@ -0,0 +1,186 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + public abstract partial class GameplayPerformancePointsCounter : RollingCounter + { + protected override bool IsRollingProportional => true; + + protected override double RollingDuration => 1000; + + private const float alpha_when_invalid = 0.3f; + + [Resolved] + private ScoreProcessor scoreProcessor { get; set; } + + [Resolved] + private GameplayState gameplayState { get; set; } + + [CanBeNull] + private List timedAttributes; + + private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); + + private JudgementResult lastJudgement; + private PerformanceCalculator performanceCalculator; + private ScoreInfo scoreInfo; + + protected GameplayPerformancePointsCounter() + { + Current.Value = DisplayedCount = 0; + } + + private Mod[] clonedMods; + + [BackgroundDependencyLoader] + private void load(BeatmapDifficultyCache difficultyCache) + { + DrawableCount.Alpha = alpha_when_invalid; + + if (gameplayState != null) + { + performanceCalculator = gameplayState.Ruleset.CreatePerformanceCalculator(); + clonedMods = gameplayState.Mods.Select(m => m.DeepClone()).ToArray(); + + scoreInfo = new ScoreInfo(gameplayState.Score.ScoreInfo.BeatmapInfo, gameplayState.Score.ScoreInfo.Ruleset) { Mods = clonedMods }; + + var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); + difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) + .ContinueWith(task => Schedule(() => + { + timedAttributes = task.GetResultSafely(); + + IsValid = true; + + if (lastJudgement != null) + onJudgementChanged(lastJudgement); + }), TaskContinuationOptions.OnlyOnRanToCompletion); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (scoreProcessor != null) + { + scoreProcessor.NewJudgement += onJudgementChanged; + scoreProcessor.JudgementReverted += onJudgementChanged; + } + + if (gameplayState?.LastJudgementResult.Value != null) + onJudgementChanged(gameplayState.LastJudgementResult.Value); + } + + private bool isValid; + + protected bool IsValid + { + set + { + if (value == isValid) + return; + + isValid = value; + DrawableCount.FadeTo(isValid ? 1 : alpha_when_invalid, 1000, Easing.OutQuint); + } + } + + private void onJudgementChanged(JudgementResult judgement) + { + lastJudgement = judgement; + + var attrib = getAttributeAtTime(judgement); + + if (gameplayState == null || attrib == null || scoreProcessor == null) + { + IsValid = false; + return; + } + + scoreProcessor.PopulateScore(scoreInfo); + Current.Value = (int)Math.Round(performanceCalculator?.Calculate(scoreInfo, attrib).Total ?? 0, MidpointRounding.AwayFromZero); + IsValid = true; + } + + [CanBeNull] + private DifficultyAttributes getAttributeAtTime(JudgementResult judgement) + { + if (timedAttributes == null || timedAttributes.Count == 0) + return null; + + int attribIndex = timedAttributes.BinarySearch(new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); + if (attribIndex < 0) + attribIndex = ~attribIndex - 1; + + return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes; + } + + protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (scoreProcessor != null) + { + scoreProcessor.NewJudgement -= onJudgementChanged; + scoreProcessor.JudgementReverted -= onJudgementChanged; + } + + loadCancellationSource?.Cancel(); + } + + // TODO: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap. + private class GameplayWorkingBeatmap : WorkingBeatmap + { + private readonly IBeatmap gameplayBeatmap; + + public GameplayWorkingBeatmap(IBeatmap gameplayBeatmap) + : base(gameplayBeatmap.BeatmapInfo, null) + { + this.gameplayBeatmap = gameplayBeatmap; + } + + public override IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods, CancellationToken cancellationToken) + => gameplayBeatmap; + + protected override IBeatmap GetBeatmap() => gameplayBeatmap; + + public override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + + protected internal override ISkin GetSkin() => throw new NotImplementedException(); + + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); + } + } +} diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 82f116b4ae..76feae88af 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -1,175 +1,31 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Resources.Localisation.Web; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Play.HUD { - public partial class PerformancePointsCounter : RollingCounter, ISerialisableDrawable + // todo: this should be renamed to DefaultPerformancePointsCounter, or probably even TrianglesPerformancePointsCounter once all other triangles components are renamed accordingly. + public partial class PerformancePointsCounter : GameplayPerformancePointsCounter, ISerialisableDrawable { public bool UsesFixedAnchor { get; set; } - protected override bool IsRollingProportional => true; - - protected override double RollingDuration => 1000; - - private const float alpha_when_invalid = 0.3f; - - [Resolved] - private ScoreProcessor scoreProcessor { get; set; } - - [Resolved] - private GameplayState gameplayState { get; set; } - - [CanBeNull] - private List timedAttributes; - - private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); - - private JudgementResult lastJudgement; - private PerformanceCalculator performanceCalculator; - private ScoreInfo scoreInfo; - - public PerformancePointsCounter() - { - Current.Value = DisplayedCount = 0; - } - - private Mod[] clonedMods; - [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + private void load(OsuColour colours) { Colour = colours.BlueLighter; - - if (gameplayState != null) - { - performanceCalculator = gameplayState.Ruleset.CreatePerformanceCalculator(); - clonedMods = gameplayState.Mods.Select(m => m.DeepClone()).ToArray(); - - scoreInfo = new ScoreInfo(gameplayState.Score.ScoreInfo.BeatmapInfo, gameplayState.Score.ScoreInfo.Ruleset) { Mods = clonedMods }; - - var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); - difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) - .ContinueWith(task => Schedule(() => - { - timedAttributes = task.GetResultSafely(); - - IsValid = true; - - if (lastJudgement != null) - onJudgementChanged(lastJudgement); - }), TaskContinuationOptions.OnlyOnRanToCompletion); - } } - protected override void LoadComplete() - { - base.LoadComplete(); - - if (scoreProcessor != null) - { - scoreProcessor.NewJudgement += onJudgementChanged; - scoreProcessor.JudgementReverted += onJudgementChanged; - } - - if (gameplayState?.LastJudgementResult.Value != null) - onJudgementChanged(gameplayState.LastJudgementResult.Value); - } - - private bool isValid; - - protected bool IsValid - { - set - { - if (value == isValid) - return; - - isValid = value; - DrawableCount.FadeTo(isValid ? 1 : alpha_when_invalid, 1000, Easing.OutQuint); - } - } - - private void onJudgementChanged(JudgementResult judgement) - { - lastJudgement = judgement; - - var attrib = getAttributeAtTime(judgement); - - if (gameplayState == null || attrib == null || scoreProcessor == null) - { - IsValid = false; - return; - } - - scoreProcessor.PopulateScore(scoreInfo); - Current.Value = (int)Math.Round(performanceCalculator?.Calculate(scoreInfo, attrib).Total ?? 0, MidpointRounding.AwayFromZero); - IsValid = true; - } - - [CanBeNull] - private DifficultyAttributes getAttributeAtTime(JudgementResult judgement) - { - if (timedAttributes == null || timedAttributes.Count == 0) - return null; - - int attribIndex = timedAttributes.BinarySearch(new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); - if (attribIndex < 0) - attribIndex = ~attribIndex - 1; - - return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes; - } - - protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); - - protected override IHasText CreateText() => new TextComponent - { - Alpha = alpha_when_invalid - }; - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (scoreProcessor != null) - { - scoreProcessor.NewJudgement -= onJudgementChanged; - scoreProcessor.JudgementReverted -= onJudgementChanged; - } - - loadCancellationSource?.Cancel(); - } + protected override IHasText CreateText() => new TextComponent(); private partial class TextComponent : CompositeDrawable, IHasText { @@ -209,30 +65,5 @@ namespace osu.Game.Screens.Play.HUD }; } } - - // TODO: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap. - private class GameplayWorkingBeatmap : WorkingBeatmap - { - private readonly IBeatmap gameplayBeatmap; - - public GameplayWorkingBeatmap(IBeatmap gameplayBeatmap) - : base(gameplayBeatmap.BeatmapInfo, null) - { - this.gameplayBeatmap = gameplayBeatmap; - } - - public override IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods, CancellationToken cancellationToken) - => gameplayBeatmap; - - protected override IBeatmap GetBeatmap() => gameplayBeatmap; - - public override Texture GetBackground() => throw new NotImplementedException(); - - protected override Track GetBeatmapTrack() => throw new NotImplementedException(); - - protected internal override ISkin GetSkin() => throw new NotImplementedException(); - - public override Stream GetStream(string storagePath) => throw new NotImplementedException(); - } } } From 56eeb117ce2011ad4b80cf32c3b015eb482d7c9b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 09:00:16 +0300 Subject: [PATCH 007/207] Add "Argon" performance points counter --- .../TestScenePerformancePointsCounter.cs | 5 +- .../Play/ArgonPerformancePointsCounter.cs | 62 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs index 9622caabf5..9b8346cf12 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs @@ -16,7 +16,6 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Tests.Visual.Gameplay @@ -30,7 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay private int iteration; private Bindable lastJudgementResult = new Bindable(); - private PerformancePointsCounter counter; + private ArgonPerformancePointsCounter counter; [SetUpSteps] public void SetUpSteps() => AddStep("create components", () => @@ -66,7 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void createCounter() => AddStep("Create counter", () => { - dependencyContainer.Child = counter = new PerformancePointsCounter + dependencyContainer.Child = counter = new ArgonPerformancePointsCounter { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs b/osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs new file mode 100644 index 0000000000..f0e0e1f0a4 --- /dev/null +++ b/osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs @@ -0,0 +1,62 @@ +// 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.LocalisationExtensions; +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 osu.Game.Resources.Localisation.Web; +using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play +{ + public partial class ArgonPerformancePointsCounter : GameplayPerformancePointsCounter, ISerialisableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + } + + protected override IHasText CreateText() => new TextComponent(); + + private partial class TextComponent : CompositeDrawable, IHasText + { + private readonly OsuSpriteText text; + + public LocalisableString Text + { + get => text.Text; + set => text.Text = value; + } + + public TextComponent() + { + AutoSizeAxes = Axes.Both; + + InternalChild = new FillFlowContainer + { + Direction = FillDirection.Vertical, + Children = new[] + { + new OsuSpriteText + { + Text = BeatmapsetsStrings.ShowScoreboardHeaderspp.ToUpper(), + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), + }, + text = new OsuSpriteText + { + Font = OsuFont.Torus.With(size: 16.8f, weight: FontWeight.Regular), + } + } + }; + } + } + } +} From 50af1574cfea82a89933fc7fb969d4a94726a766 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 09:10:56 +0300 Subject: [PATCH 008/207] Add "Argon" background wedges --- osu.Game/Screens/Play/HUD/ArgonComboWedge.cs | 27 +++++++ osu.Game/Screens/Play/HUD/ArgonRightWedge.cs | 29 +++++++ osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs | 83 ++++++++++++++++++++ osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs | 69 ++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 osu.Game/Screens/Play/HUD/ArgonComboWedge.cs create mode 100644 osu.Game/Screens/Play/HUD/ArgonRightWedge.cs create mode 100644 osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs create mode 100644 osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonComboWedge.cs b/osu.Game/Screens/Play/HUD/ArgonComboWedge.cs new file mode 100644 index 0000000000..6da3727505 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonComboWedge.cs @@ -0,0 +1,27 @@ +// 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.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonComboWedge : CompositeDrawable, ISerialisableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + InternalChild = new ArgonWedgePiece + { + WedgeWidth = { Value = 186 }, + WedgeHeight = { Value = 33 }, + }; + } + } +} diff --git a/osu.Game/Screens/Play/HUD/ArgonRightWedge.cs b/osu.Game/Screens/Play/HUD/ArgonRightWedge.cs new file mode 100644 index 0000000000..6d01fecf13 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonRightWedge.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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonRightWedge : CompositeDrawable, ISerialisableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + InternalChild = new ArgonWedgePiece + { + WedgeWidth = { Value = 274 }, + WedgeHeight = { Value = 40 }, + InvertShear = { Value = true }, + EndOpacity = 0.5f, + }; + } + } +} diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs b/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs new file mode 100644 index 0000000000..dc0130fb8e --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonScoreWedge.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.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonScoreWedge : CompositeDrawable, ISerialisableDrawable + { + private SliderPath barPath = null!; + + private const float main_path_radius = 1f; + + public bool UsesFixedAnchor { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + const float bar_length = 430 - main_path_radius * 2; + const float bar_top = 0; + const float bar_bottom = 80; + const float curve_start = bar_length - 105; + const float curve_end = bar_length - 35; + + const float curve_smoothness = 10; + + Vector2 diagonalDir = (new Vector2(curve_end, bar_top) - new Vector2(curve_start, bar_bottom)).Normalized(); + + barPath = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, bar_bottom), PathType.Linear), + new PathControlPoint(new Vector2(curve_start - curve_smoothness, bar_bottom), PathType.Bezier), + new PathControlPoint(new Vector2(curve_start, bar_bottom)), + new PathControlPoint(new Vector2(curve_start, bar_bottom) + diagonalDir * curve_smoothness, PathType.Linear), + new PathControlPoint(new Vector2(curve_end, bar_top) - diagonalDir * curve_smoothness, PathType.Bezier), + new PathControlPoint(new Vector2(curve_end, bar_top)), + new PathControlPoint(new Vector2(curve_end + curve_smoothness, bar_top), PathType.Linear), + new PathControlPoint(new Vector2(bar_length, bar_top)), + }); + + var vertices = new List(); + barPath.GetPathToProgress(vertices, 0, 1); + + InternalChildren = new Drawable[] + { + new ArgonWedgePiece + { + WedgeWidth = { Value = 380 }, + WedgeHeight = { Value = 72 }, + }, + new ArgonWedgePiece + { + WedgeWidth = { Value = 380 }, + WedgeHeight = { Value = 72 }, + Position = new Vector2(4, 5) + }, + new SmoothPath + { + Colour = Color4.White, + PathRadius = 1f, + Vertices = vertices, + }, + new Circle + { + Y = bar_bottom - 1.5f + main_path_radius, + Size = new Vector2(300f, 3f), + } + }; + } + } +} diff --git a/osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs b/osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs new file mode 100644 index 0000000000..085e4c738f --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Configuration; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonWedgePiece : CompositeDrawable, ISerialisableDrawable + { + public bool UsesFixedAnchor { get; set; } + + public float EndOpacity { get; init; } = 0.25f; + + [SettingSource("Wedge width")] + public BindableFloat WedgeWidth { get; } = new BindableFloat(400f) + { + MinValue = 0f, + MaxValue = 500f, + Precision = 1f, + }; + + [SettingSource("Wedge height")] + public BindableFloat WedgeHeight { get; } = new BindableFloat(100f) + { + MinValue = 0f, + MaxValue = 500f, + Precision = 1f, + }; + + [SettingSource("Inverted shear")] + public BindableBool InvertShear { get; } = new BindableBool(); + + public ArgonWedgePiece() + { + CornerRadius = 10f; + } + + [BackgroundDependencyLoader] + private void load() + { + Masking = true; + Shear = new Vector2(0.8f, 0f); + + InternalChild = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#66CCFF").Opacity(0.0f), Color4Extensions.FromHex("#66CCFF").Opacity(EndOpacity)), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + WedgeWidth.BindValueChanged(v => Width = v.NewValue, true); + WedgeHeight.BindValueChanged(v => Height = v.NewValue, true); + InvertShear.BindValueChanged(v => Shear = new Vector2(0.8f, 0f) * (v.NewValue ? -1 : 1), true); + } + } +} From df80b4dcab1fdda9f120376bfcbbdb26f96ccb1b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 09:11:27 +0300 Subject: [PATCH 009/207] Update "Argon" health display specifications to match design Also adds a little white bar next to the display to match with the components below it (score/accuracy/combo counters). --- .../TestSceneSkinnableHealthDisplay.cs | 2 +- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 68 +++++++++++-------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index 8d3eee2445..e9092bba1b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f) }; + protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f), BarLength = { Value = 1f } }; protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay { Scale = new Vector2(0.6f) }; protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay { Scale = new Vector2(0.6f) }; diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 793d43f7ef..cc69acf8d6 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Shapes; using osu.Framework.Layout; using osu.Framework.Threading; using osu.Framework.Utils; @@ -28,7 +29,7 @@ namespace osu.Game.Screens.Play.HUD public bool UsesFixedAnchor { get; set; } [SettingSource("Bar height")] - public BindableFloat BarHeight { get; } = new BindableFloat(20) + public BindableFloat BarHeight { get; } = new BindableFloat(30) { MinValue = 0, MaxValue = 64, @@ -36,7 +37,7 @@ namespace osu.Game.Screens.Play.HUD }; [SettingSource("Bar length")] - public BindableFloat BarLength { get; } = new BindableFloat(0.98f) + public BindableFloat BarLength { get; } = new BindableFloat(0.35f) { MinValue = 0.2f, MaxValue = 1, @@ -100,36 +101,45 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new Container + InternalChildren = new[] { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + new Circle { - background = new BackgroundPath + Position = new Vector2(-4f, main_path_radius - 1.5f), + Size = new Vector2(50f, 3f), + }, + new Container + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Left = 50f }, + Children = new Drawable[] { - PathRadius = main_path_radius, - }, - glowBar = new BarPath - { - BarColour = Color4.White, - GlowColour = main_bar_glow_colour, - Blending = BlendingParameters.Additive, - Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), - PathRadius = 40f, - // Kinda hacky, but results in correct positioning with increased path radius. - Margin = new MarginPadding(-30f), - GlowPortion = 0.9f, - }, - mainBar = new BarPath - { - AutoSizeAxes = Axes.None, - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - BarColour = main_bar_colour, - GlowColour = main_bar_glow_colour, - PathRadius = main_path_radius, - GlowPortion = 0.6f, - }, + background = new BackgroundPath + { + PathRadius = main_path_radius, + }, + glowBar = new BarPath + { + BarColour = Color4.White, + GlowColour = main_bar_glow_colour, + Blending = BlendingParameters.Additive, + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), + PathRadius = 40f, + // Kinda hacky, but results in correct positioning with increased path radius. + Margin = new MarginPadding(-30f), + GlowPortion = 0.9f, + }, + mainBar = new BarPath + { + AutoSizeAxes = Axes.None, + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + BarColour = main_bar_colour, + GlowColour = main_bar_glow_colour, + PathRadius = main_path_radius, + GlowPortion = 0.6f, + }, + } } }; } From e119e045e47a5023531b5d41dfd4ed80fcc5e427 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 09:12:24 +0300 Subject: [PATCH 010/207] Update "Argon" skin components layout and use the new components --- osu.Game/Skinning/ArgonSkin.cs | 91 +++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 26f2edf1aa..ee4a74ab6a 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -115,49 +115,67 @@ namespace osu.Game.Skinning var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container => { var health = container.OfType().FirstOrDefault(); - var score = container.OfType().FirstOrDefault(); - var accuracy = container.OfType().FirstOrDefault(); - var combo = container.OfType().FirstOrDefault(); - var ppCounter = container.OfType().FirstOrDefault(); + var scoreWedge = container.OfType().FirstOrDefault(); + var score = container.OfType().FirstOrDefault(); + var accuracy = container.OfType().FirstOrDefault(); + var comboWedge = container.OfType().FirstOrDefault(); + var combo = container.OfType().FirstOrDefault(); + var rightWedge = container.OfType().FirstOrDefault(); + var ppCounter = container.OfType().FirstOrDefault(); var songProgress = container.OfType().FirstOrDefault(); var keyCounter = container.OfType().FirstOrDefault(); - if (score != null) + if (health != null) { - score.Anchor = Anchor.TopCentre; - score.Origin = Anchor.TopCentre; - // elements default to beneath the health bar - const float vertical_offset = 30; + const float components_x_offset = 50; - const float horizontal_padding = 20; + health.Anchor = Anchor.TopLeft; + health.Origin = Anchor.TopLeft; + health.Y = 15; - score.Position = new Vector2(0, vertical_offset); - - if (health != null) + if (scoreWedge != null) { - health.Origin = Anchor.TopCentre; - health.Anchor = Anchor.TopCentre; - health.Y = 5; + scoreWedge.Position = new Vector2(-50, 50); + + if (score != null) + { + score.Scale = new Vector2(0.5f); + score.Position = new Vector2(components_x_offset, scoreWedge.Y + 15); + } + + if (accuracy != null) + { + // +4 to vertically align the accuracy counter with the score counter. + accuracy.Position = new Vector2(components_x_offset + 4, scoreWedge.Y + 45); + accuracy.Anchor = Anchor.TopLeft; + accuracy.Origin = Anchor.TopLeft; + } } - if (ppCounter != null) + if (comboWedge != null) { - ppCounter.Y = score.Position.Y + ppCounter.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).Y - 4; - ppCounter.Origin = Anchor.TopCentre; - ppCounter.Anchor = Anchor.TopCentre; - } - - if (accuracy != null) - { - accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, vertical_offset + 5); - accuracy.Origin = Anchor.TopRight; - accuracy.Anchor = Anchor.TopCentre; + comboWedge.Position = new Vector2(-12, 130); if (combo != null) { - combo.Position = new Vector2(accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 + horizontal_padding, vertical_offset + 5); - combo.Anchor = Anchor.TopCentre; + combo.Anchor = Anchor.TopLeft; + combo.Origin = Anchor.TopLeft; + combo.Position = new Vector2(components_x_offset, comboWedge.Y - 2); + } + } + + if (rightWedge != null) + { + rightWedge.Anchor = Anchor.TopRight; + rightWedge.Origin = Anchor.TopRight; + rightWedge.Position = new Vector2(180, 20); + + if (ppCounter != null) + { + ppCounter.Anchor = Anchor.TopRight; + ppCounter.Origin = Anchor.TopRight; + ppCounter.Position = new Vector2(rightWedge.X - 240, rightWedge.Y + 8); } } @@ -201,15 +219,18 @@ namespace osu.Game.Skinning { Children = new Drawable[] { - new DefaultComboCounter(), - new DefaultScoreCounter(), - new DefaultAccuracyCounter(), new ArgonHealthDisplay(), + new ArgonScoreWedge(), + new ArgonScoreCounter(), + new ArgonAccuracyCounter(), + new ArgonComboWedge(), + new ArgonComboCounter(), + new ArgonRightWedge(), + new ArgonPerformancePointsCounter(), + new BarHitErrorMeter(), + new BarHitErrorMeter(), new ArgonSongProgress(), new ArgonKeyCounterDisplay(), - new BarHitErrorMeter(), - new BarHitErrorMeter(), - new PerformancePointsCounter() } }; From 07e7d533bfa211536e199ac92c308cfc6f5bfd45 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 10:01:27 +0300 Subject: [PATCH 011/207] Bake external scale factors into glyph scale --- osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs | 2 +- osu.Game/Skinning/ArgonSkin.cs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index cfda34578b..03635f2914 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -113,7 +113,7 @@ namespace osu.Game.Screens.Play.HUD if (texture == null) return null; - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.25f); + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f); } public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index ee4a74ab6a..07d9afffac 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -139,10 +139,7 @@ namespace osu.Game.Skinning scoreWedge.Position = new Vector2(-50, 50); if (score != null) - { - score.Scale = new Vector2(0.5f); score.Position = new Vector2(components_x_offset, scoreWedge.Y + 15); - } if (accuracy != null) { From 841c5bb4bd9d78a2dc191271d3a09a60446c35e6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Oct 2023 11:04:55 +0300 Subject: [PATCH 012/207] Add modified skin for serialisation test coverage --- .../Archives/modified-argon-pro-20231026.osk | Bin 0 -> 2066 bytes osu.Game.Tests/Skins/SkinDeserialisationTest.cs | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Resources/Archives/modified-argon-pro-20231026.osk diff --git a/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231026.osk b/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231026.osk new file mode 100644 index 0000000000000000000000000000000000000000..e349c14d7403a13b92ae0b5fed2ee449273a199b GIT binary patch literal 2066 zcmZ`)2T&7e77m0Enlyuiw!j8OTtozAAuN$1H30&F&;*VWFo7g=G@x`?nj)@LF(5@o ziUv%GA}CGiaIm3BmEKvJQe?fX=ba;W`~UOipZCvvZ@%}aF``j#>Tx_9IEfXkDYGelFnDBX>BKD|+L0&LpEeSmEZuGOG>$7d&!V{S7tZ7%*`+j4*zLTXPoRVl)r{c&!KkoM3Y^cf*rR zY%d#-iGE}fjuaI5h2&}ul02d*9J#(H7T=>GzocizKa+N9)r)qTaN&k@l79YdV)HR` zZiv|6>CK7Ebmu`KhDo$hfb$60KpkSZnJXdqcAwI>ymRhCE{zr$F+*Sa%sL?8usQq} zy&7F22$f;T>xE%IzXFvlmR0FTR4e6|p@6NTsa&~*9UsLPQ1fOkoX1O;ld~0KLX-(d zA&RVS=9M;#(8US8^_QY0eST*O)N&H_$Tq2A=GPfJ$Pod<2ksyhGJ zYgD{Dcf96(wzmTopV8%{C<8|;Bp`Fn4r=8oYrLYIBL1Oc*H0s8ZMsj;cN@-cBjSf$ zVqI3=8ebKP`M|JXz6-t}U7NKhbKoG%%p#i9#t#6bqUy1fc290tOR>_(2d2FL)bh~u zO<*{^$2nlj+Nanx#t{2Z97Q&P>o|q1L=FXtjHV%UZtp#*5+U*R6-6UW%p0nucyVJS z?ZS@V(Dy8sf%7uw_Okd%OT19$2BW4bbal|OeqQW5hyoV7}Z^JrM!F{CTcA$^BeUCNBWXW6a9r(;SZ^?rv-JWKgevj`9I?= zfC)O}9>^tHLBU+>n@x#h+@`vRha=qQZ>YW65$Rk4S$c=}y)+b~j1*igaIK4t3zhV{ z4t`gJ}2RdBRg>vakF9U(|Hg=;)PthU1jv7&X;C=Q;G?hnLXJ0TVwBKM2t`i#TO zS&VPiEiVgXj1>R*iT&Cp^Dtu;D#!JE6{YXFQ%i&Yg+>?y5696=lgA)Sy`+dd^`|lI=*Ylt4 zb`*7VLwv;m08ukvQUBc+N1;at_%~=IyFmW`I6oTWXgPh0p#l1z;yMaGs^T|rIQLOa T(MS;c4*&qMuL_%9_ZRP9@fu;D literal 0 HcmV?d00001 diff --git a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs index 98008a003d..b66a5b7862 100644 --- a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs +++ b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs @@ -54,7 +54,9 @@ namespace osu.Game.Tests.Skins // Covers key counters "Archives/modified-argon-pro-20230618.osk", // Covers "Argon" health display - "Archives/modified-argon-pro-20231001.osk" + "Archives/modified-argon-pro-20231001.osk", + // Covers "Argon" score/combo/accuracy/pp counters, and wedges + "Archives/modified-argon-pro-20231026.osk" }; /// From 4ae9b40a7806d6637840c25349282f07ef6d66e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Oct 2023 16:35:14 +0900 Subject: [PATCH 013/207] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bf0a1fcbad..4f03f398d5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 818432fab4279b6691286483b6be88eb663e84c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 2 Nov 2023 19:27:55 +0900 Subject: [PATCH 014/207] Fix non-classic osu! combo not matching expectations --- osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs | 9 +++++++++ osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs | 11 ----------- .../Objects/SliderTailCircle.cs | 16 ++++++++++++++-- osu.Game/Rulesets/Scoring/HitResult.cs | 3 +++ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs index ddbbb300ca..88a34fcb8f 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs @@ -3,6 +3,8 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects @@ -43,5 +45,12 @@ namespace osu.Game.Rulesets.Osu.Objects } protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + public override Judgement CreateJudgement() => new SliderEndJudgement(); + + public class SliderEndJudgement : OsuJudgement + { + public override HitResult MaxResult => HitResult.LargeTickHit; + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs index cca86361c2..e95cfd369d 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs @@ -1,10 +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.Judgements; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Scoring; - namespace osu.Game.Rulesets.Osu.Objects { public class SliderRepeat : SliderEndCircle @@ -13,12 +9,5 @@ namespace osu.Game.Rulesets.Osu.Objects : base(slider) { } - - public override Judgement CreateJudgement() => new SliderRepeatJudgement(); - - public class SliderRepeatJudgement : OsuJudgement - { - public override HitResult MaxResult => HitResult.LargeTickHit; - } } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index 54d2afb444..357476ed30 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -9,16 +9,28 @@ namespace osu.Game.Rulesets.Osu.Objects { public class SliderTailCircle : SliderEndCircle { + /// + /// Whether to treat this as a normal for judgement purposes. + /// If false, this will be judged as a instead. + /// + public bool ClassicSliderBehaviour; + public SliderTailCircle(Slider slider) : base(slider) { } - public override Judgement CreateJudgement() => new SliderTailJudgement(); + public override Judgement CreateJudgement() => ClassicSliderBehaviour ? new LegacyTailJudgement() : new TailJudgement(); - public class SliderTailJudgement : OsuJudgement + public class LegacyTailJudgement : OsuJudgement { public override HitResult MaxResult => HitResult.SmallTickHit; } + + public class TailJudgement : SliderEndJudgement + { + public override HitResult MaxResult => HitResult.LargeTickHit; + public override HitResult MinResult => HitResult.IgnoreMiss; + } } } diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index fed338b012..6380b73558 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -350,6 +350,9 @@ namespace osu.Game.Rulesets.Scoring if (maxResult.IsBonus() && minResult != HitResult.IgnoreMiss) throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.IgnoreMiss} is the only valid minimum result for a {maxResult} judgement."); + if (minResult == HitResult.IgnoreMiss) + return; + if (maxResult == HitResult.LargeTickHit && minResult != HitResult.LargeTickMiss) throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.LargeTickMiss} is the only valid minimum result for a {maxResult} judgement."); From f0f595ca40ab02cd0637a50ce0246d05423fc0c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 2 Nov 2023 19:52:49 +0900 Subject: [PATCH 015/207] Continue to play spinner bonus sounds when MAX display occurs --- .../Objects/Drawables/DrawableSpinner.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 2e9a4d92ec..aa43532f65 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -303,6 +303,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult; + private int lastMaxSamplePlayback; + private void updateBonusScore() { if (ticks.Count == 0) @@ -322,7 +324,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables var tick = ticks.FirstOrDefault(t => !t.Result.HasResult); // tick may be null if we've hit the spin limit. - tick?.TriggerResult(true); + if (tick == null) + { + // we still want to play a sound. this will probably be a new sound in the future, but for now let's continue playing the bonus sound. + // round robin to avoid hitting playback concurrency. + tick = ticks.OfType().Skip(lastMaxSamplePlayback++ % HitObject.MaximumBonusSpins).First(); + tick.PlaySamples(); + } + else + tick.TriggerResult(true); completedFullSpins.Value++; } From 9f11a04cc731159c6d2c0e4f7084238f6bd88360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 30 Oct 2023 13:24:01 +0100 Subject: [PATCH 016/207] Generalise notion of 'touch device' mod --- osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 9 +-------- osu.Game/Rulesets/Mods/ModTouchDevice.cs | 16 ++++++++++++++++ osu.Game/Rulesets/Ruleset.cs | 2 ++ 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ModTouchDevice.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index fd5c46a226..abb67c519b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -1,18 +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.Framework.Localisation; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModTouchDevice : Mod + public class OsuModTouchDevice : ModTouchDevice { - public override string Name => "Touch Device"; - public override string Acronym => "TD"; - public override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen."; - public override double ScoreMultiplier => 1; - - public override ModType Type => ModType.System; } } diff --git a/osu.Game/Rulesets/Mods/ModTouchDevice.cs b/osu.Game/Rulesets/Mods/ModTouchDevice.cs new file mode 100644 index 0000000000..2daea8ea5d --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModTouchDevice.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. + +using osu.Framework.Localisation; + +namespace osu.Game.Rulesets.Mods +{ + public class ModTouchDevice : Mod + { + public sealed override string Name => "Touch Device"; + public sealed override string Acronym => "TD"; + public sealed override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen."; + public sealed override double ScoreMultiplier => 1; + public sealed override ModType Type => ModType.System; + } +} diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index be0d757e06..f8aa6c9f57 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -204,6 +204,8 @@ namespace osu.Game.Rulesets public ModAutoplay? GetAutoplayMod() => CreateMod(); + public ModTouchDevice? GetTouchDeviceMod() => CreateMod(); + /// /// Create a transformer which adds lookups specific to a ruleset to skin sources. /// From 68efb3c110aa3617fb801cb8afe6b7fb15afcba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 19:25:57 +0100 Subject: [PATCH 017/207] Mark `ModTouchDevice` as always valid for submission --- osu.Game/Rulesets/Mods/IMod.cs | 7 +++++++ osu.Game/Rulesets/Mods/Mod.cs | 4 ++++ osu.Game/Rulesets/Mods/ModTouchDevice.cs | 1 + 3 files changed, 12 insertions(+) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index ce2d123884..744d02a4fa 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -59,6 +59,13 @@ namespace osu.Game.Rulesets.Mods /// bool ValidForMultiplayerAsFreeMod { get; } + /// + /// Indicates that this mod is always permitted in scenarios wherein a user is submitting a score regardless of other circumstances. + /// Intended for mods that are informational in nature and do not really affect gameplay by themselves, + /// but are more of a gauge of increased/decreased difficulty due to the user's configuration (e.g. ). + /// + bool AlwaysValidForSubmission { get; } + /// /// Create a fresh instance based on this mod. /// diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 49c2fdd394..775f6a0ed4 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -156,6 +156,10 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool ValidForMultiplayerAsFreeMod => true; + /// + [JsonIgnore] + public virtual bool AlwaysValidForSubmission => false; + /// /// Whether this mod requires configuration to apply changes to the game. /// diff --git a/osu.Game/Rulesets/Mods/ModTouchDevice.cs b/osu.Game/Rulesets/Mods/ModTouchDevice.cs index 2daea8ea5d..0865603c8c 100644 --- a/osu.Game/Rulesets/Mods/ModTouchDevice.cs +++ b/osu.Game/Rulesets/Mods/ModTouchDevice.cs @@ -12,5 +12,6 @@ namespace osu.Game.Rulesets.Mods public sealed override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen."; public sealed override double ScoreMultiplier => 1; public sealed override ModType Type => ModType.System; + public sealed override bool AlwaysValidForSubmission => true; } } From 980c900f43aa958e25d519e687a4a2e224d2399c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 30 Oct 2023 14:08:56 +0100 Subject: [PATCH 018/207] Add component for game-wide touch detection --- .../Navigation/TestSceneScreenNavigation.cs | 22 ++++++++++ osu.Game/Configuration/SessionStatics.cs | 10 ++++- osu.Game/Input/TouchInputInterceptor.cs | 41 +++++++++++++++++++ osu.Game/OsuGameBase.cs | 2 + 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Input/TouchInputInterceptor.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 9e743ef336..f4318da403 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -12,6 +12,7 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -835,6 +836,27 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("exit dialog is shown", () => Game.Dependencies.Get().CurrentDialog is ConfirmExitDialog); } + [Test] + public void TestTouchScreenDetection() + { + AddStep("touch logo", () => + { + var button = Game.ChildrenOfType().Single(); + var touch = new Touch(TouchSource.Touch1, button.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddAssert("touch screen detected active", () => Game.Dependencies.Get().Get(Static.TouchInputActive), () => Is.True); + + AddStep("click settings button", () => + { + var button = Game.ChildrenOfType().Last(); + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + AddAssert("touch screen detected inactive", () => Game.Dependencies.Get().Get(Static.TouchInputActive), () => Is.False); + } + private Func playToResults() { var player = playToCompletion(); diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 5e2f0c2128..0fc2076a7e 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -4,6 +4,7 @@ #nullable disable using osu.Game.Graphics.UserInterface; +using osu.Game.Input; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -24,6 +25,7 @@ namespace osu.Game.Configuration SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); + SetDefault(Static.TouchInputActive, false); } /// @@ -63,6 +65,12 @@ namespace osu.Game.Configuration /// The last playback time in milliseconds of an on/off sample (from ). /// Used to debounce on/off sounds game-wide to avoid volume saturation, especially in activating mod presets with many mods. /// - LastModSelectPanelSamplePlaybackTime + LastModSelectPanelSamplePlaybackTime, + + /// + /// Whether the last positional input received was a touch input. + /// Used in touchscreen detection scenarios (). + /// + TouchInputActive, } } diff --git a/osu.Game/Input/TouchInputInterceptor.cs b/osu.Game/Input/TouchInputInterceptor.cs new file mode 100644 index 0000000000..377d3c6d9f --- /dev/null +++ b/osu.Game/Input/TouchInputInterceptor.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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Framework.Input.StateChanges; +using osu.Game.Configuration; +using osuTK; + +namespace osu.Game.Input +{ + /// + /// Intercepts all positional input events and sets the appropriate value + /// for consumption by particular game screens. + /// + public partial class TouchInputInterceptor : Component + { + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + + [Resolved] + private SessionStatics statics { get; set; } = null!; + + protected override bool Handle(UIEvent e) + { + switch (e) + { + case MouseEvent: + if (e.CurrentState.Mouse.LastSource is not ISourcedFromTouch) + statics.SetValue(Static.TouchInputActive, false); + break; + + case TouchEvent: + statics.SetValue(Static.TouchInputActive, true); + break; + } + + return false; + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1f46eb0c0d..e4376d1c02 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -407,6 +407,8 @@ namespace osu.Game }) }); + base.Content.Add(new TouchInputInterceptor()); + KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider); KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets); From 2f6ff893b550a18ec1f4791d45c96c5dcb3e99cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 30 Oct 2023 14:38:20 +0100 Subject: [PATCH 019/207] Automatically activate and deactivate touch device mod in song select --- .../Navigation/TestSceneScreenNavigation.cs | 42 ++++++++++++++ osu.Game/Rulesets/Mods/ModTouchDevice.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 1 + .../Select/SongSelectTouchInputHandler.cs | 56 +++++++++++++++++++ osu.Game/Utils/ModUtils.cs | 2 +- 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Screens/Select/SongSelectTouchInputHandler.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index f4318da403..0ba8f7136d 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -855,6 +855,48 @@ namespace osu.Game.Tests.Visual.Navigation InputManager.Click(MouseButton.Left); }); AddAssert("touch screen detected inactive", () => Game.Dependencies.Get().Get(Static.TouchInputActive), () => Is.False); + + AddStep("close settings sidebar", () => InputManager.Key(Key.Escape)); + + PushAndConfirm(() => new TestPlaySongSelect()); + AddStep("switch to osu! ruleset", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.Number1); + InputManager.ReleaseKey(Key.LControl); + }); + AddStep("touch beatmap wedge", () => + { + var wedge = Game.ChildrenOfType().Single(); + var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddUntilStep("touch device mod activated", () => Game.SelectedMods.Value, () => Has.One.InstanceOf()); + + AddStep("switch to mania ruleset", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.Number4); + InputManager.ReleaseKey(Key.LControl); + }); + AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf()); + AddStep("touch beatmap wedge", () => + { + var wedge = Game.ChildrenOfType().Single(); + var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf()); + + AddStep("switch to osu! ruleset", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.Number1); + InputManager.ReleaseKey(Key.LControl); + }); + AddUntilStep("touch device mod activated", () => Game.SelectedMods.Value, () => Has.One.InstanceOf()); } private Func playToResults() diff --git a/osu.Game/Rulesets/Mods/ModTouchDevice.cs b/osu.Game/Rulesets/Mods/ModTouchDevice.cs index 0865603c8c..f532e7b19d 100644 --- a/osu.Game/Rulesets/Mods/ModTouchDevice.cs +++ b/osu.Game/Rulesets/Mods/ModTouchDevice.cs @@ -5,7 +5,7 @@ using osu.Framework.Localisation; namespace osu.Game.Rulesets.Mods { - public class ModTouchDevice : Mod + public class ModTouchDevice : Mod, IApplicableMod { public sealed override string Name => "Touch Device"; public sealed override string Acronym => "TD"; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dfea4e3794..74454713d1 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -279,6 +279,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, }, + new SongSelectTouchInputHandler() }); if (ShowFooter) diff --git a/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs b/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs new file mode 100644 index 0000000000..bf4761e871 --- /dev/null +++ b/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs @@ -0,0 +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.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Screens.Select +{ + public partial class SongSelectTouchInputHandler : Component + { + [Resolved] + private Bindable ruleset { get; set; } = null!; + + [Resolved] + private Bindable> mods { get; set; } = null!; + + private IBindable touchActive = null!; + + [BackgroundDependencyLoader] + private void load(SessionStatics statics) + { + touchActive = statics.GetBindable(Static.TouchInputActive); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ruleset.BindValueChanged(_ => updateState()); + mods.BindValueChanged(_ => updateState()); + touchActive.BindValueChanged(_ => updateState()); + updateState(); + } + + private void updateState() + { + var touchDeviceMod = ruleset.Value.CreateInstance().GetTouchDeviceMod(); + + if (touchDeviceMod == null) + return; + + bool touchDeviceModEnabled = mods.Value.Any(mod => mod is ModTouchDevice); + + if (touchActive.Value && !touchDeviceModEnabled) + mods.Value = mods.Value.Append(touchDeviceMod).ToArray(); + if (!touchActive.Value && touchDeviceModEnabled) + mods.Value = mods.Value.Where(mod => mod is not ModTouchDevice).ToArray(); + } + } +} diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index edf9cc80da..1bd60fcdde 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -121,7 +121,7 @@ namespace osu.Game.Utils if (!CheckCompatibleSet(mods, out invalidMods)) return false; - return checkValid(mods, m => m.Type != ModType.System && m.HasImplementation, out invalidMods); + return checkValid(mods, m => m.HasImplementation, out invalidMods); } /// From 29d4d81eaab886e0f99bd2c579a1425a41b4b17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 Oct 2023 19:56:51 +0200 Subject: [PATCH 020/207] Add test scene for basic touchscreen detection scenarios --- .../Mods/TestSceneOsuModTouchDevice.cs | 134 ++++++++++++++++++ .../Navigation/TestSceneScreenNavigation.cs | 5 +- 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs new file mode 100644 index 0000000000..9c84bcc241 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -0,0 +1,134 @@ +// 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.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Overlays; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Tests.Visual; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public partial class TestSceneOsuModTouchDevice : PlayerTestScene + { + private TestOnScreenDisplay testOnScreenDisplay = null!; + + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => + new OsuBeatmap + { + HitObjects = + { + new HitCircle + { + Position = OsuPlayfield.BASE_SIZE / 2, + StartTime = 0, + }, + new HitCircle + { + Position = OsuPlayfield.BASE_SIZE / 2, + StartTime = 5000, + }, + }, + Breaks = + { + new BreakPeriod(2000, 3000) + } + }; + + [BackgroundDependencyLoader] + private void load() + { + Add(testOnScreenDisplay = new TestOnScreenDisplay()); + Dependencies.CacheAs(testOnScreenDisplay); + } + + public override void SetUpSteps() + { + base.SetUpSteps(); + AddStep("reset OSD toast count", () => testOnScreenDisplay.ToastCount = 0); + } + + [Test] + public void TestFirstObjectTouched() + { + AddUntilStep("wait until 0 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0).Within(500)); + AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); + AddUntilStep("wait until 0", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); + AddStep("touch circle", () => + { + var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + AddAssert("no toasts displayed", () => testOnScreenDisplay.ToastCount, () => Is.Zero); + } + + [Test] + public void TestTouchDuringBreak() + { + AddUntilStep("wait until 2000 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000).Within(500)); + AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); + AddUntilStep("wait until 2000", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000)); + AddStep("touch playfield", () => + { + var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddAssert("touch device mod not activated", () => Player.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); + AddAssert("no toasts displayed", () => testOnScreenDisplay.ToastCount, () => Is.Zero); + } + + [Test] + public void TestSecondObjectTouched() + { + // ensure mouse is active (and that it's not suppressed due to touches in previous tests) + AddStep("click mouse", () => InputManager.Click(MouseButton.Left)); + + AddUntilStep("wait until 0 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0).Within(500)); + AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); + AddUntilStep("wait until 0", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); + AddStep("click circle", () => + { + InputManager.MoveMouseTo(Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + AddAssert("touch device mod not activated", () => Player.Mods.Value, () => Has.No.InstanceOf()); + + AddStep("speed back up", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 1); + AddUntilStep("wait until 5000 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000).Within(500)); + AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); + AddUntilStep("wait until 5000", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000)); + AddStep("touch playfield", () => + { + var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + AddAssert("toast displayed", () => testOnScreenDisplay.ToastCount, () => Is.EqualTo(1)); + } + + private partial class TestOnScreenDisplay : OnScreenDisplay + { + public int ToastCount { get; set; } + + protected override void DisplayTemporarily(Drawable toDisplay) + { + base.DisplayTemporarily(toDisplay); + ToastCount++; + } + } + } +} diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 0ba8f7136d..10c1104376 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -858,7 +858,10 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("close settings sidebar", () => InputManager.Key(Key.Escape)); - PushAndConfirm(() => new TestPlaySongSelect()); + Screens.Select.SongSelect songSelect = null; + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); + AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); + AddStep("switch to osu! ruleset", () => { InputManager.PressKey(Key.LControl); From f2df02b60f7e78a4e1fad2c591c24dfe14271c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 Oct 2023 20:27:07 +0200 Subject: [PATCH 021/207] Automatically activate touch device mod in player --- .../Mods/TestSceneOsuModTouchDevice.cs | 18 +++++- .../Overlays/OSD/TouchDeviceDetectedToast.cs | 13 ++++ osu.Game/Screens/Play/Player.cs | 1 + .../Screens/Play/PlayerTouchInputHandler.cs | 61 +++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs create mode 100644 osu.Game/Screens/Play/PlayerTouchInputHandler.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index 9c84bcc241..5134265741 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -1,12 +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.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; +using osu.Game.Configuration; +using osu.Game.Input; using osu.Game.Overlays; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Mods; @@ -19,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public partial class TestSceneOsuModTouchDevice : PlayerTestScene { + [Resolved] + private SessionStatics statics { get; set; } = null!; + private TestOnScreenDisplay testOnScreenDisplay = null!; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); @@ -49,18 +55,26 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods private void load() { Add(testOnScreenDisplay = new TestOnScreenDisplay()); + Add(new TouchInputInterceptor()); Dependencies.CacheAs(testOnScreenDisplay); } public override void SetUpSteps() { - base.SetUpSteps(); AddStep("reset OSD toast count", () => testOnScreenDisplay.ToastCount = 0); + AddStep("reset static", () => statics.SetValue(Static.TouchInputActive, false)); + base.SetUpSteps(); } [Test] - public void TestFirstObjectTouched() + public void TestUserAlreadyHasTouchDeviceActive() { + // it is presumed that a previous screen (i.e. song select) will set this up + AddStep("set up touchscreen user", () => + { + Player.Score.ScoreInfo.Mods = Player.Score.ScoreInfo.Mods.Append(new OsuModTouchDevice()).ToArray(); + statics.SetValue(Static.TouchInputActive, true); + }); AddUntilStep("wait until 0 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0).Within(500)); AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); AddUntilStep("wait until 0", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); diff --git a/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs b/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs new file mode 100644 index 0000000000..0b5e3fffbe --- /dev/null +++ b/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs @@ -0,0 +1,13 @@ +// 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.Overlays.OSD +{ + public partial class TouchDeviceDetectedToast : Toast + { + public TouchDeviceDetectedToast() + : base("osu!", "Touch device detected", "Touch Device mod applied to score") + { + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8c7fc551ba..a67e16912d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -285,6 +285,7 @@ namespace osu.Game.Screens.Play fadeOut(true); }, }, + new PlayerTouchInputHandler() }); if (cancellationToken.IsCancellationRequested) diff --git a/osu.Game/Screens/Play/PlayerTouchInputHandler.cs b/osu.Game/Screens/Play/PlayerTouchInputHandler.cs new file mode 100644 index 0000000000..0252ee9503 --- /dev/null +++ b/osu.Game/Screens/Play/PlayerTouchInputHandler.cs @@ -0,0 +1,61 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Overlays; +using osu.Game.Overlays.OSD; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Screens.Play +{ + public partial class PlayerTouchInputHandler : Component + { + [Resolved] + private Player player { get; set; } = null!; + + [Resolved] + private GameplayState gameplayState { get; set; } = null!; + + [Resolved] + private OnScreenDisplay? onScreenDisplay { get; set; } + + private IBindable touchActive = new BindableBool(); + + [BackgroundDependencyLoader] + private void load(SessionStatics statics) + { + touchActive = statics.GetBindable(Static.TouchInputActive); + touchActive.BindValueChanged(_ => updateState()); + } + + private void updateState() + { + if (!touchActive.Value) + return; + + if (gameplayState.HasPassed || gameplayState.HasFailed || gameplayState.HasQuit) + return; + + if (gameplayState.Score.ScoreInfo.Mods.OfType().Any()) + return; + + if (player.IsBreakTime.Value) + return; + + var touchDeviceMod = gameplayState.Ruleset.GetTouchDeviceMod(); + if (touchDeviceMod == null) + return; + + onScreenDisplay?.Display(new TouchDeviceDetectedToast()); + + // TODO: this is kinda crude. `Player` (probably rightly so) assumes immutability of mods. + // this probably should be shown immediately on screen in the HUD, + // which means that immutability will probably need to be revisited. + player.Score.ScoreInfo.Mods = player.Score.ScoreInfo.Mods.Append(touchDeviceMod).ToArray(); + } + } +} From 21a4da463b11d557a815581cdb09121a9ea0a94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 31 Oct 2023 13:47:50 +0100 Subject: [PATCH 022/207] Add failing test covering crash scenario --- .../Navigation/TestSceneScreenNavigation.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 10c1104376..a1fd8768b6 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -900,6 +900,38 @@ namespace osu.Game.Tests.Visual.Navigation InputManager.ReleaseKey(Key.LControl); }); AddUntilStep("touch device mod activated", () => Game.SelectedMods.Value, () => Has.One.InstanceOf()); + + AddStep("click beatmap wedge", () => + { + InputManager.MoveMouseTo(Game.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf()); + + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + AddStep("press enter", () => InputManager.Key(Key.Enter)); + + Player player = null; + + AddUntilStep("wait for player", () => + { + DismissAnyNotifications(); + return (player = Game.ScreenStack.CurrentScreen as Player) != null; + }); + + AddUntilStep("wait for track playing", () => Game.Beatmap.Value.Track.IsRunning); + + AddStep("touch", () => + { + var touch = new Touch(TouchSource.Touch2, Game.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddUntilStep("touch device mod added to score", () => player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + + AddStep("exit player", () => player.Exit()); + AddUntilStep("touch device mod still active", () => Game.SelectedMods.Value, () => Has.One.InstanceOf()); } private Func playToResults() From ef555ed0cf442b98cb139de435e0e0815468c05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 30 Oct 2023 15:48:00 +0100 Subject: [PATCH 023/207] Fix test failures --- osu.Game.Tests/Mods/ModUtilsTest.cs | 6 +++--- .../Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs | 1 + osu.Game/Overlays/Mods/SelectAllModsButton.cs | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index aa41fd830b..9107ddd1ae 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -147,11 +147,11 @@ namespace osu.Game.Tests.Mods new Mod[] { new OsuModDeflate(), new OsuModApproachDifferent() }, new[] { typeof(OsuModDeflate), typeof(OsuModApproachDifferent) } }, - // system mod. + // system mod not applicable in lazer. new object[] { - new Mod[] { new OsuModHidden(), new OsuModTouchDevice() }, - new[] { typeof(OsuModTouchDevice) } + new Mod[] { new OsuModHidden(), new ModScoreV2() }, + new[] { typeof(ModScoreV2) } }, // multi mod. new object[] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index 66ba908879..f1674401cd 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -133,6 +133,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private bool assertAllAvailableModsSelected() { var allAvailableMods = availableMods.Value + .Where(pair => pair.Key != ModType.System) .SelectMany(pair => pair.Value) .Where(mod => mod.UserPlayable && mod.HasImplementation) .ToList(); diff --git a/osu.Game/Overlays/Mods/SelectAllModsButton.cs b/osu.Game/Overlays/Mods/SelectAllModsButton.cs index bb61cdc35d..b6b3051a0d 100644 --- a/osu.Game/Overlays/Mods/SelectAllModsButton.cs +++ b/osu.Game/Overlays/Mods/SelectAllModsButton.cs @@ -41,6 +41,7 @@ namespace osu.Game.Overlays.Mods private void updateEnabledState() { Enabled.Value = availableMods.Value + .Where(pair => pair.Key != ModType.System) .SelectMany(pair => pair.Value) .Any(modState => !modState.Active.Value && modState.Visible); } From c588f434e5f51bee3683c6114bc951fda40fb50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 31 Oct 2023 13:54:18 +0100 Subject: [PATCH 024/207] Fix song select touch handler causing crashes when song select is suspended --- osu.Game/Screens/Select/SongSelectTouchInputHandler.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs b/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs index bf4761e871..5c27e59c5b 100644 --- a/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs +++ b/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs @@ -32,14 +32,18 @@ namespace osu.Game.Screens.Select { base.LoadComplete(); - ruleset.BindValueChanged(_ => updateState()); - mods.BindValueChanged(_ => updateState()); - touchActive.BindValueChanged(_ => updateState()); + ruleset.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + mods.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + mods.BindDisabledChanged(_ => Scheduler.AddOnce(updateState)); + touchActive.BindValueChanged(_ => Scheduler.AddOnce(updateState)); updateState(); } private void updateState() { + if (mods.Disabled) + return; + var touchDeviceMod = ruleset.Value.CreateInstance().GetTouchDeviceMod(); if (touchDeviceMod == null) From 4532d0ecdf12abd32b3d64cbab3b9e975f34c63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 19:48:34 +0100 Subject: [PATCH 025/207] Add logging & debug facility for touch input interceptor --- osu.Game/Input/TouchInputInterceptor.cs | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game/Input/TouchInputInterceptor.cs b/osu.Game/Input/TouchInputInterceptor.cs index 377d3c6d9f..4e6177c70c 100644 --- a/osu.Game/Input/TouchInputInterceptor.cs +++ b/osu.Game/Input/TouchInputInterceptor.cs @@ -1,12 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; +using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges; +using osu.Framework.Logging; using osu.Game.Configuration; using osuTK; +using osuTK.Input; namespace osu.Game.Input { @@ -23,19 +27,42 @@ namespace osu.Game.Input protected override bool Handle(UIEvent e) { + bool touchInputWasActive = statics.Get(Static.TouchInputActive); + switch (e) { case MouseEvent: if (e.CurrentState.Mouse.LastSource is not ISourcedFromTouch) + { + if (touchInputWasActive) + Logger.Log($@"Touch input deactivated due to received {e.GetType().ReadableName()}", LoggingTarget.Input); statics.SetValue(Static.TouchInputActive, false); + } + break; case TouchEvent: + if (!touchInputWasActive) + Logger.Log($@"Touch input activated due to received {e.GetType().ReadableName()}", LoggingTarget.Input); statics.SetValue(Static.TouchInputActive, true); break; + + case KeyDownEvent keyDown: + if (keyDown.Key == Key.T && keyDown.ControlPressed && keyDown.ShiftPressed) + debugToggleTouchInputActive(); + break; } return false; } + + [Conditional("TOUCH_INPUT_DEBUG")] + private void debugToggleTouchInputActive() + { + bool oldValue = statics.Get(Static.TouchInputActive); + bool newValue = !oldValue; + Logger.Log($@"Debug-toggling touch input to {(newValue ? @"active" : @"inactive")}", LoggingTarget.Input, LogLevel.Debug); + statics.SetValue(Static.TouchInputActive, newValue); + } } } From 3a6d65d395c1bf3c0e30429fec90be5e4fedb7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 19:51:21 +0100 Subject: [PATCH 026/207] Touch up `PlayerTouchInputHandler` --- osu.Game/Screens/Play/PlayerTouchInputHandler.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerTouchInputHandler.cs b/osu.Game/Screens/Play/PlayerTouchInputHandler.cs index 0252ee9503..728b3c846b 100644 --- a/osu.Game/Screens/Play/PlayerTouchInputHandler.cs +++ b/osu.Game/Screens/Play/PlayerTouchInputHandler.cs @@ -9,6 +9,7 @@ using osu.Game.Configuration; using osu.Game.Overlays; using osu.Game.Overlays.OSD; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play { @@ -50,11 +51,15 @@ namespace osu.Game.Screens.Play if (touchDeviceMod == null) return; - onScreenDisplay?.Display(new TouchDeviceDetectedToast()); + // do not show the toast if the user hasn't hit anything yet. + // we're kind of assuming that the user just switches to touch for gameplay + // and we don't want to spam them with obvious toasts. + if (gameplayState.ScoreProcessor.HitEvents.Any(ev => ev.Result.IsHit())) + onScreenDisplay?.Display(new TouchDeviceDetectedToast()); - // TODO: this is kinda crude. `Player` (probably rightly so) assumes immutability of mods. - // this probably should be shown immediately on screen in the HUD, - // which means that immutability will probably need to be revisited. + // `Player` (probably rightly so) assumes immutability of mods, + // so this will not be shown immediately on the mod display in the top right. + // if this is to change, the mod immutability should be revisited. player.Score.ScoreInfo.Mods = player.Score.ScoreInfo.Mods.Append(touchDeviceMod).ToArray(); } } From d25b54c06d660455484762a011c162c6cb68dfd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 22:14:38 +0100 Subject: [PATCH 027/207] Split test into two They would fail on CI when written as one, and I don't care why. --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index a1fd8768b6..c6a668a714 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -837,7 +837,7 @@ namespace osu.Game.Tests.Visual.Navigation } [Test] - public void TestTouchScreenDetection() + public void TestTouchScreenDetectionAtSongSelect() { AddStep("touch logo", () => { @@ -859,8 +859,9 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("close settings sidebar", () => InputManager.Key(Key.Escape)); Screens.Select.SongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestPlaySongSelect()); - AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); + AddRepeatStep("go to solo", () => InputManager.Key(Key.P), 3); + AddUntilStep("wait for song select", () => (songSelect = Game.ScreenStack.CurrentScreen as Screens.Select.SongSelect) != null); + AddUntilStep("wait for beatmap sets loaded", () => songSelect.BeatmapSetsLoaded); AddStep("switch to osu! ruleset", () => { @@ -907,10 +908,15 @@ namespace osu.Game.Tests.Visual.Navigation InputManager.Click(MouseButton.Left); }); AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf()); + } + [Test] + public void TestTouchScreenDetectionInGame() + { + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - AddStep("press enter", () => InputManager.Key(Key.Enter)); + AddStep("select", () => InputManager.Key(Key.Enter)); Player player = null; From 8784dd42c0c4c1aba37cb0cc4f5cda8055811a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 22:20:17 +0100 Subject: [PATCH 028/207] Do not attempt to turn on Touch Device in song select with autoplay active --- osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 +- osu.Game/Rulesets/Mods/ModTouchDevice.cs | 2 ++ .../Screens/Select/SongSelectTouchInputHandler.cs | 11 ++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 973fcffba8..302cdf69c0 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mods public sealed override bool ValidForMultiplayer => false; public sealed override bool ValidForMultiplayerAsFreeMod => false; - public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModAdaptiveSpeed) }; + public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModAdaptiveSpeed), typeof(ModTouchDevice) }; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; diff --git a/osu.Game/Rulesets/Mods/ModTouchDevice.cs b/osu.Game/Rulesets/Mods/ModTouchDevice.cs index f532e7b19d..c29ff28f1a 100644 --- a/osu.Game/Rulesets/Mods/ModTouchDevice.cs +++ b/osu.Game/Rulesets/Mods/ModTouchDevice.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.Localisation; namespace osu.Game.Rulesets.Mods @@ -13,5 +14,6 @@ namespace osu.Game.Rulesets.Mods public sealed override double ScoreMultiplier => 1; public sealed override ModType Type => ModType.System; public sealed override bool AlwaysValidForSubmission => true; + public sealed override Type[] IncompatibleMods => new[] { typeof(ICreateReplayData) }; } } diff --git a/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs b/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs index 5c27e59c5b..973dc12e12 100644 --- a/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs +++ b/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Utils; namespace osu.Game.Screens.Select { @@ -52,7 +53,15 @@ namespace osu.Game.Screens.Select bool touchDeviceModEnabled = mods.Value.Any(mod => mod is ModTouchDevice); if (touchActive.Value && !touchDeviceModEnabled) - mods.Value = mods.Value.Append(touchDeviceMod).ToArray(); + { + var candidateMods = mods.Value.Append(touchDeviceMod).ToArray(); + + if (!ModUtils.CheckCompatibleSet(candidateMods, out _)) + return; + + mods.Value = candidateMods; + } + if (!touchActive.Value && touchDeviceModEnabled) mods.Value = mods.Value.Where(mod => mod is not ModTouchDevice).ToArray(); } From 0dd0a84312e224bb2cb5396cdc479e2b04688000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 22:26:52 +0100 Subject: [PATCH 029/207] Move player touch handler to `SubmittingPlayer` Shouldn't be there in `ReplayPlayer`. --- osu.Game/Screens/Play/Player.cs | 1 - osu.Game/Screens/Play/SubmittingPlayer.cs | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a67e16912d..8c7fc551ba 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -285,7 +285,6 @@ namespace osu.Game.Screens.Play fadeOut(true); }, }, - new PlayerTouchInputHandler() }); if (cancellationToken.IsCancellationRequested) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index a75546f835..c34902aeb8 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -44,6 +44,12 @@ namespace osu.Game.Screens.Play { } + [BackgroundDependencyLoader] + private void load() + { + AddInternal(new PlayerTouchInputHandler()); + } + protected override void LoadAsyncComplete() { base.LoadAsyncComplete(); From 8e9006b5d5c263cf79fed1e74532e7de5dfca122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 22:27:16 +0100 Subject: [PATCH 030/207] Declare Touch Device incompatible with Autopilot With Autopilot active, Touch Device no longer matters. --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 3 ++- osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 3 +++ osu.Game/Rulesets/Mods/ModTouchDevice.cs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 3841c9c716..56bf0e08e9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -33,7 +33,8 @@ namespace osu.Game.Rulesets.Osu.Mods typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised), - typeof(OsuModRepel) + typeof(OsuModRepel), + typeof(ModTouchDevice) }; public bool PerformFail() => false; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index abb67c519b..f1468d414e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.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 System; +using System.Linq; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTouchDevice : ModTouchDevice { + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); } } diff --git a/osu.Game/Rulesets/Mods/ModTouchDevice.cs b/osu.Game/Rulesets/Mods/ModTouchDevice.cs index c29ff28f1a..a5dfe5448c 100644 --- a/osu.Game/Rulesets/Mods/ModTouchDevice.cs +++ b/osu.Game/Rulesets/Mods/ModTouchDevice.cs @@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Mods public sealed override double ScoreMultiplier => 1; public sealed override ModType Type => ModType.System; public sealed override bool AlwaysValidForSubmission => true; - public sealed override Type[] IncompatibleMods => new[] { typeof(ICreateReplayData) }; + public override Type[] IncompatibleMods => new[] { typeof(ICreateReplayData) }; } } From a613292802ce0fe5f53b31de761e6dcdf3135113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 Nov 2023 23:44:40 +0100 Subject: [PATCH 031/207] Fix unknown mod test failure --- osu.Game/Screens/Play/SubmittingPlayer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index c34902aeb8..e018b8dab3 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -47,6 +47,12 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { + if (DrawableRuleset == null) + { + // base load must have failed (e.g. due to an unknown mod); bail. + return; + } + AddInternal(new PlayerTouchInputHandler()); } From a78fab0e7d74d41b58ecefbe74854370ffee08c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 Nov 2023 00:17:29 +0100 Subject: [PATCH 032/207] Do not hardcode ruleset name in touch device detection toast --- osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs | 6 ++++-- osu.Game/Screens/Play/PlayerTouchInputHandler.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs b/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs index 0b5e3fffbe..266e10ab1f 100644 --- a/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs +++ b/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs @@ -1,12 +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.Game.Rulesets; + namespace osu.Game.Overlays.OSD { public partial class TouchDeviceDetectedToast : Toast { - public TouchDeviceDetectedToast() - : base("osu!", "Touch device detected", "Touch Device mod applied to score") + public TouchDeviceDetectedToast(RulesetInfo ruleset) + : base(ruleset.Name, "Touch device detected", "Touch Device mod applied to score") { } } diff --git a/osu.Game/Screens/Play/PlayerTouchInputHandler.cs b/osu.Game/Screens/Play/PlayerTouchInputHandler.cs index 728b3c846b..a7d41de105 100644 --- a/osu.Game/Screens/Play/PlayerTouchInputHandler.cs +++ b/osu.Game/Screens/Play/PlayerTouchInputHandler.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Play // we're kind of assuming that the user just switches to touch for gameplay // and we don't want to spam them with obvious toasts. if (gameplayState.ScoreProcessor.HitEvents.Any(ev => ev.Result.IsHit())) - onScreenDisplay?.Display(new TouchDeviceDetectedToast()); + onScreenDisplay?.Display(new TouchDeviceDetectedToast(gameplayState.Ruleset.RulesetInfo)); // `Player` (probably rightly so) assumes immutability of mods, // so this will not be shown immediately on the mod display in the top right. From a70bfca501312bc587c230064a6269b20a714c82 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Thu, 2 Nov 2023 09:16:25 +0100 Subject: [PATCH 033/207] added usergrid for tooltip --- osu.Game/Users/Drawables/ClickableAvatar.cs | 33 ++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 677a8fff36..0ed9f56cc7 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -3,16 +3,27 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; +using osuTK; namespace osu.Game.Users.Drawables { - public partial class ClickableAvatar : OsuClickableContainer + public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip { + public ITooltip GetCustomTooltip() => new UserGridPanelTooltip(); + + public UserGridPanel TooltipContent => new UserGridPanel(user!) + { + Width = 300 + }; + public override LocalisableString TooltipText { get @@ -67,5 +78,25 @@ namespace osu.Game.Users.Drawables return base.OnClick(e); } + + private partial class UserGridPanelTooltip : VisibilityContainer, ITooltip + { + private UserGridPanel? displayedUser; + + protected override void PopIn() + { + Child = displayedUser; + this.FadeIn(20, Easing.OutQuint); + } + + protected override void PopOut() => this.FadeOut(80, Easing.OutQuint); + + public void Move(Vector2 pos) => Position = pos; + + public void SetContent(UserGridPanel userGridPanel) + { + displayedUser = userGridPanel; + } + } } } From ec290ae953c9dffec44bb71e1fcde4e0785ef843 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Sat, 4 Nov 2023 19:03:23 +0100 Subject: [PATCH 034/207] added tests --- .../Online/TestSceneUserClickableAvatar.cs | 125 ++++++++++++++++++ osu.Game/Users/Drawables/ClickableAvatar.cs | 21 --- osu.Game/Users/Drawables/UpdateableAvatar.cs | 1 - 3 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs new file mode 100644 index 0000000000..13f559ac09 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -0,0 +1,125 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Testing; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Online +{ + public partial class TestSceneUserClickableAvatar : OsuManualInputManagerTestScene + { + [SetUp] + public void SetUp() => Schedule(() => + { + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Spacing = new Vector2(10f), + Children = new Drawable[] + { + new ClickableAvatar(new APIUser + { + Username = @"flyte", Id = 3103765, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + }) + { + Width = 50, + Height = 50, + CornerRadius = 10, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), + }, + }, + new ClickableAvatar(new APIUser + { + Username = @"peppy", Id = 2, Colour = "99EB47", CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }) + { + Width = 50, + Height = 50, + CornerRadius = 10, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), + }, + }, + new ClickableAvatar(new APIUser + { + Username = @"flyte", + Id = 3103765, + CountryCode = CountryCode.JP, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", + Status = + { + Value = new UserStatusOnline() + } + }) + { + Width = 50, + Height = 50, + CornerRadius = 10, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), + }, + }, + }, + }; + }); + + [Test] + public void TestClickableAvatarHover() + { + AddStep($"click {1}. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 1) + return; + + InputManager.MoveMouseTo(targets[0]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click {2}. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 2) + return; + + InputManager.MoveMouseTo(targets[1]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click {3}. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 3) + return; + + InputManager.MoveMouseTo(targets[2]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + } + } +} diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 0ed9f56cc7..d6c6afba0b 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -1,15 +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.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Game.Graphics.Containers; -using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osuTK; @@ -24,24 +21,6 @@ namespace osu.Game.Users.Drawables Width = 300 }; - public override LocalisableString TooltipText - { - get - { - if (!Enabled.Value) - return string.Empty; - - return ShowUsernameTooltip ? (user?.Username ?? string.Empty) : ContextMenuStrings.ViewProfile; - } - set => throw new NotSupportedException(); - } - - /// - /// By default, the tooltip will show "view profile" as avatars are usually displayed next to a username. - /// Setting this to true exposes the username via tooltip for special cases where this is not true. - /// - public bool ShowUsernameTooltip { get; set; } - private readonly APIUser? user; [Resolved] diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index c659685807..58b3646995 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -74,7 +74,6 @@ namespace osu.Game.Users.Drawables { return new ClickableAvatar(user) { - ShowUsernameTooltip = showUsernameTooltip, RelativeSizeAxes = Axes.Both, }; } From 91cf237fc12fcbecc6d1345310717407f559a32e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 4 Nov 2023 02:46:28 +0300 Subject: [PATCH 035/207] Revert health display settings changes --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index cc69acf8d6..1a213ddc6f 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Play.HUD public bool UsesFixedAnchor { get; set; } [SettingSource("Bar height")] - public BindableFloat BarHeight { get; } = new BindableFloat(30) + public BindableFloat BarHeight { get; } = new BindableFloat(20) { MinValue = 0, MaxValue = 64, @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Play.HUD }; [SettingSource("Bar length")] - public BindableFloat BarLength { get; } = new BindableFloat(0.35f) + public BindableFloat BarLength { get; } = new BindableFloat(0.98f) { MinValue = 0.2f, MaxValue = 1, From 1c844a155e97059e23cdae6470401d217fa88db3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Nov 2023 01:52:12 +0300 Subject: [PATCH 036/207] Remove health line detail --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 64 ++++++++----------- osu.Game/Skinning/ArgonSkin.cs | 4 +- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 1a213ddc6f..793d43f7ef 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.Shapes; using osu.Framework.Layout; using osu.Framework.Threading; using osu.Framework.Utils; @@ -101,45 +100,36 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChildren = new[] + InternalChild = new Container { - new Circle + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Position = new Vector2(-4f, main_path_radius - 1.5f), - Size = new Vector2(50f, 3f), - }, - new Container - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Left = 50f }, - Children = new Drawable[] + background = new BackgroundPath { - background = new BackgroundPath - { - PathRadius = main_path_radius, - }, - glowBar = new BarPath - { - BarColour = Color4.White, - GlowColour = main_bar_glow_colour, - Blending = BlendingParameters.Additive, - Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), - PathRadius = 40f, - // Kinda hacky, but results in correct positioning with increased path radius. - Margin = new MarginPadding(-30f), - GlowPortion = 0.9f, - }, - mainBar = new BarPath - { - AutoSizeAxes = Axes.None, - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - BarColour = main_bar_colour, - GlowColour = main_bar_glow_colour, - PathRadius = main_path_radius, - GlowPortion = 0.6f, - }, - } + PathRadius = main_path_radius, + }, + glowBar = new BarPath + { + BarColour = Color4.White, + GlowColour = main_bar_glow_colour, + Blending = BlendingParameters.Additive, + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), + PathRadius = 40f, + // Kinda hacky, but results in correct positioning with increased path radius. + Margin = new MarginPadding(-30f), + GlowPortion = 0.9f, + }, + mainBar = new BarPath + { + AutoSizeAxes = Axes.None, + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + BarColour = main_bar_colour, + GlowColour = main_bar_glow_colour, + PathRadius = main_path_radius, + GlowPortion = 0.6f, + }, } }; } diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 07d9afffac..f850ae9cbd 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -130,8 +130,8 @@ namespace osu.Game.Skinning // elements default to beneath the health bar const float components_x_offset = 50; - health.Anchor = Anchor.TopLeft; - health.Origin = Anchor.TopLeft; + health.Anchor = Anchor.TopCentre; + health.Origin = Anchor.TopCentre; health.Y = 15; if (scoreWedge != null) From 58a830fc5b2bd749ebccd7cf1b5e0f12b6f4c117 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Nov 2023 01:55:12 +0300 Subject: [PATCH 037/207] Revert "Add "Argon" performance points counter" This reverts commit 56eeb117ce2011ad4b80cf32c3b015eb482d7c9b. --- .../TestScenePerformancePointsCounter.cs | 5 +- .../Play/ArgonPerformancePointsCounter.cs | 62 ------------------- 2 files changed, 3 insertions(+), 64 deletions(-) delete mode 100644 osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs index 9b8346cf12..9622caabf5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Tests.Visual.Gameplay @@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.Gameplay private int iteration; private Bindable lastJudgementResult = new Bindable(); - private ArgonPerformancePointsCounter counter; + private PerformancePointsCounter counter; [SetUpSteps] public void SetUpSteps() => AddStep("create components", () => @@ -65,7 +66,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void createCounter() => AddStep("Create counter", () => { - dependencyContainer.Child = counter = new ArgonPerformancePointsCounter + dependencyContainer.Child = counter = new PerformancePointsCounter { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs b/osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs deleted file mode 100644 index f0e0e1f0a4..0000000000 --- a/osu.Game/Screens/Play/ArgonPerformancePointsCounter.cs +++ /dev/null @@ -1,62 +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.Allocation; -using osu.Framework.Extensions.LocalisationExtensions; -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 osu.Game.Resources.Localisation.Web; -using osu.Game.Screens.Play.HUD; -using osu.Game.Skinning; - -namespace osu.Game.Screens.Play -{ - public partial class ArgonPerformancePointsCounter : GameplayPerformancePointsCounter, ISerialisableDrawable - { - public bool UsesFixedAnchor { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - } - - protected override IHasText CreateText() => new TextComponent(); - - private partial class TextComponent : CompositeDrawable, IHasText - { - private readonly OsuSpriteText text; - - public LocalisableString Text - { - get => text.Text; - set => text.Text = value; - } - - public TextComponent() - { - AutoSizeAxes = Axes.Both; - - InternalChild = new FillFlowContainer - { - Direction = FillDirection.Vertical, - Children = new[] - { - new OsuSpriteText - { - Text = BeatmapsetsStrings.ShowScoreboardHeaderspp.ToUpper(), - Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), - }, - text = new OsuSpriteText - { - Font = OsuFont.Torus.With(size: 16.8f, weight: FontWeight.Regular), - } - } - }; - } - } - } -} From a23dfbeab55bb3e195407bcf436444f150d5a355 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Nov 2023 01:55:13 +0300 Subject: [PATCH 038/207] Revert "Abstractify PP counter logic from the "Triangles" implementation" This reverts commit daf4a03fd092ac84955a8f63fa3c45200a28cf04. --- .../HUD/GameplayPerformancePointsCounter.cs | 186 ------------------ .../Play/HUD/PerformancePointsCounter.cs | 181 ++++++++++++++++- 2 files changed, 175 insertions(+), 192 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs diff --git a/osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs deleted file mode 100644 index a812a3e043..0000000000 --- a/osu.Game/Screens/Play/HUD/GameplayPerformancePointsCounter.cs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Textures; -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - public abstract partial class GameplayPerformancePointsCounter : RollingCounter - { - protected override bool IsRollingProportional => true; - - protected override double RollingDuration => 1000; - - private const float alpha_when_invalid = 0.3f; - - [Resolved] - private ScoreProcessor scoreProcessor { get; set; } - - [Resolved] - private GameplayState gameplayState { get; set; } - - [CanBeNull] - private List timedAttributes; - - private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); - - private JudgementResult lastJudgement; - private PerformanceCalculator performanceCalculator; - private ScoreInfo scoreInfo; - - protected GameplayPerformancePointsCounter() - { - Current.Value = DisplayedCount = 0; - } - - private Mod[] clonedMods; - - [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache difficultyCache) - { - DrawableCount.Alpha = alpha_when_invalid; - - if (gameplayState != null) - { - performanceCalculator = gameplayState.Ruleset.CreatePerformanceCalculator(); - clonedMods = gameplayState.Mods.Select(m => m.DeepClone()).ToArray(); - - scoreInfo = new ScoreInfo(gameplayState.Score.ScoreInfo.BeatmapInfo, gameplayState.Score.ScoreInfo.Ruleset) { Mods = clonedMods }; - - var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); - difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) - .ContinueWith(task => Schedule(() => - { - timedAttributes = task.GetResultSafely(); - - IsValid = true; - - if (lastJudgement != null) - onJudgementChanged(lastJudgement); - }), TaskContinuationOptions.OnlyOnRanToCompletion); - } - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - if (scoreProcessor != null) - { - scoreProcessor.NewJudgement += onJudgementChanged; - scoreProcessor.JudgementReverted += onJudgementChanged; - } - - if (gameplayState?.LastJudgementResult.Value != null) - onJudgementChanged(gameplayState.LastJudgementResult.Value); - } - - private bool isValid; - - protected bool IsValid - { - set - { - if (value == isValid) - return; - - isValid = value; - DrawableCount.FadeTo(isValid ? 1 : alpha_when_invalid, 1000, Easing.OutQuint); - } - } - - private void onJudgementChanged(JudgementResult judgement) - { - lastJudgement = judgement; - - var attrib = getAttributeAtTime(judgement); - - if (gameplayState == null || attrib == null || scoreProcessor == null) - { - IsValid = false; - return; - } - - scoreProcessor.PopulateScore(scoreInfo); - Current.Value = (int)Math.Round(performanceCalculator?.Calculate(scoreInfo, attrib).Total ?? 0, MidpointRounding.AwayFromZero); - IsValid = true; - } - - [CanBeNull] - private DifficultyAttributes getAttributeAtTime(JudgementResult judgement) - { - if (timedAttributes == null || timedAttributes.Count == 0) - return null; - - int attribIndex = timedAttributes.BinarySearch(new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); - if (attribIndex < 0) - attribIndex = ~attribIndex - 1; - - return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes; - } - - protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (scoreProcessor != null) - { - scoreProcessor.NewJudgement -= onJudgementChanged; - scoreProcessor.JudgementReverted -= onJudgementChanged; - } - - loadCancellationSource?.Cancel(); - } - - // TODO: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap. - private class GameplayWorkingBeatmap : WorkingBeatmap - { - private readonly IBeatmap gameplayBeatmap; - - public GameplayWorkingBeatmap(IBeatmap gameplayBeatmap) - : base(gameplayBeatmap.BeatmapInfo, null) - { - this.gameplayBeatmap = gameplayBeatmap; - } - - public override IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods, CancellationToken cancellationToken) - => gameplayBeatmap; - - protected override IBeatmap GetBeatmap() => gameplayBeatmap; - - public override Texture GetBackground() => throw new NotImplementedException(); - - protected override Track GetBeatmapTrack() => throw new NotImplementedException(); - - protected internal override ISkin GetSkin() => throw new NotImplementedException(); - - public override Stream GetStream(string storagePath) => throw new NotImplementedException(); - } - } -} diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 76feae88af..82f116b4ae 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -1,31 +1,175 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Resources.Localisation.Web; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Play.HUD { - // todo: this should be renamed to DefaultPerformancePointsCounter, or probably even TrianglesPerformancePointsCounter once all other triangles components are renamed accordingly. - public partial class PerformancePointsCounter : GameplayPerformancePointsCounter, ISerialisableDrawable + public partial class PerformancePointsCounter : RollingCounter, ISerialisableDrawable { public bool UsesFixedAnchor { get; set; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override bool IsRollingProportional => true; + + protected override double RollingDuration => 1000; + + private const float alpha_when_invalid = 0.3f; + + [Resolved] + private ScoreProcessor scoreProcessor { get; set; } + + [Resolved] + private GameplayState gameplayState { get; set; } + + [CanBeNull] + private List timedAttributes; + + private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); + + private JudgementResult lastJudgement; + private PerformanceCalculator performanceCalculator; + private ScoreInfo scoreInfo; + + public PerformancePointsCounter() { - Colour = colours.BlueLighter; + Current.Value = DisplayedCount = 0; } - protected override IHasText CreateText() => new TextComponent(); + private Mod[] clonedMods; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + { + Colour = colours.BlueLighter; + + if (gameplayState != null) + { + performanceCalculator = gameplayState.Ruleset.CreatePerformanceCalculator(); + clonedMods = gameplayState.Mods.Select(m => m.DeepClone()).ToArray(); + + scoreInfo = new ScoreInfo(gameplayState.Score.ScoreInfo.BeatmapInfo, gameplayState.Score.ScoreInfo.Ruleset) { Mods = clonedMods }; + + var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); + difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) + .ContinueWith(task => Schedule(() => + { + timedAttributes = task.GetResultSafely(); + + IsValid = true; + + if (lastJudgement != null) + onJudgementChanged(lastJudgement); + }), TaskContinuationOptions.OnlyOnRanToCompletion); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (scoreProcessor != null) + { + scoreProcessor.NewJudgement += onJudgementChanged; + scoreProcessor.JudgementReverted += onJudgementChanged; + } + + if (gameplayState?.LastJudgementResult.Value != null) + onJudgementChanged(gameplayState.LastJudgementResult.Value); + } + + private bool isValid; + + protected bool IsValid + { + set + { + if (value == isValid) + return; + + isValid = value; + DrawableCount.FadeTo(isValid ? 1 : alpha_when_invalid, 1000, Easing.OutQuint); + } + } + + private void onJudgementChanged(JudgementResult judgement) + { + lastJudgement = judgement; + + var attrib = getAttributeAtTime(judgement); + + if (gameplayState == null || attrib == null || scoreProcessor == null) + { + IsValid = false; + return; + } + + scoreProcessor.PopulateScore(scoreInfo); + Current.Value = (int)Math.Round(performanceCalculator?.Calculate(scoreInfo, attrib).Total ?? 0, MidpointRounding.AwayFromZero); + IsValid = true; + } + + [CanBeNull] + private DifficultyAttributes getAttributeAtTime(JudgementResult judgement) + { + if (timedAttributes == null || timedAttributes.Count == 0) + return null; + + int attribIndex = timedAttributes.BinarySearch(new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); + if (attribIndex < 0) + attribIndex = ~attribIndex - 1; + + return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes; + } + + protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); + + protected override IHasText CreateText() => new TextComponent + { + Alpha = alpha_when_invalid + }; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (scoreProcessor != null) + { + scoreProcessor.NewJudgement -= onJudgementChanged; + scoreProcessor.JudgementReverted -= onJudgementChanged; + } + + loadCancellationSource?.Cancel(); + } private partial class TextComponent : CompositeDrawable, IHasText { @@ -65,5 +209,30 @@ namespace osu.Game.Screens.Play.HUD }; } } + + // TODO: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap. + private class GameplayWorkingBeatmap : WorkingBeatmap + { + private readonly IBeatmap gameplayBeatmap; + + public GameplayWorkingBeatmap(IBeatmap gameplayBeatmap) + : base(gameplayBeatmap.BeatmapInfo, null) + { + this.gameplayBeatmap = gameplayBeatmap; + } + + public override IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods, CancellationToken cancellationToken) + => gameplayBeatmap; + + protected override IBeatmap GetBeatmap() => gameplayBeatmap; + + public override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + + protected internal override ISkin GetSkin() => throw new NotImplementedException(); + + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); + } } } From 6c3169a0ed6e9f0f9e1e7d668993e04861f63540 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Nov 2023 01:56:45 +0300 Subject: [PATCH 039/207] Remove PP wedge and logic for gameplay layout --- osu.Game/Screens/Play/HUD/ArgonRightWedge.cs | 29 -------------------- osu.Game/Skinning/ArgonSkin.cs | 18 ------------ 2 files changed, 47 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/ArgonRightWedge.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonRightWedge.cs b/osu.Game/Screens/Play/HUD/ArgonRightWedge.cs deleted file mode 100644 index 6d01fecf13..0000000000 --- a/osu.Game/Screens/Play/HUD/ArgonRightWedge.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.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - public partial class ArgonRightWedge : CompositeDrawable, ISerialisableDrawable - { - public bool UsesFixedAnchor { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - AutoSizeAxes = Axes.Both; - - InternalChild = new ArgonWedgePiece - { - WedgeWidth = { Value = 274 }, - WedgeHeight = { Value = 40 }, - InvertShear = { Value = true }, - EndOpacity = 0.5f, - }; - } - } -} diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index f850ae9cbd..3262812d24 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -120,8 +120,6 @@ namespace osu.Game.Skinning var accuracy = container.OfType().FirstOrDefault(); var comboWedge = container.OfType().FirstOrDefault(); var combo = container.OfType().FirstOrDefault(); - var rightWedge = container.OfType().FirstOrDefault(); - var ppCounter = container.OfType().FirstOrDefault(); var songProgress = container.OfType().FirstOrDefault(); var keyCounter = container.OfType().FirstOrDefault(); @@ -162,20 +160,6 @@ namespace osu.Game.Skinning } } - if (rightWedge != null) - { - rightWedge.Anchor = Anchor.TopRight; - rightWedge.Origin = Anchor.TopRight; - rightWedge.Position = new Vector2(180, 20); - - if (ppCounter != null) - { - ppCounter.Anchor = Anchor.TopRight; - ppCounter.Origin = Anchor.TopRight; - ppCounter.Position = new Vector2(rightWedge.X - 240, rightWedge.Y + 8); - } - } - var hitError = container.OfType().FirstOrDefault(); if (hitError != null) @@ -222,8 +206,6 @@ namespace osu.Game.Skinning new ArgonAccuracyCounter(), new ArgonComboWedge(), new ArgonComboCounter(), - new ArgonRightWedge(), - new ArgonPerformancePointsCounter(), new BarHitErrorMeter(), new BarHitErrorMeter(), new ArgonSongProgress(), From 77f5a4cdf53fca660a71140b84c9897439ddbf2e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Nov 2023 02:01:26 +0300 Subject: [PATCH 040/207] Update skin deserialisation archive --- .../Archives/modified-argon-pro-20231026.osk | Bin 2066 -> 0 bytes .../Archives/modified-argon-pro-20231105.osk | Bin 0 -> 1899 bytes osu.Game.Tests/Skins/SkinDeserialisationTest.cs | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 osu.Game.Tests/Resources/Archives/modified-argon-pro-20231026.osk create mode 100644 osu.Game.Tests/Resources/Archives/modified-argon-pro-20231105.osk diff --git a/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231026.osk b/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231026.osk deleted file mode 100644 index e349c14d7403a13b92ae0b5fed2ee449273a199b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2066 zcmZ`)2T&7e77m0Enlyuiw!j8OTtozAAuN$1H30&F&;*VWFo7g=G@x`?nj)@LF(5@o ziUv%GA}CGiaIm3BmEKvJQe?fX=ba;W`~UOipZCvvZ@%}aF``j#>Tx_9IEfXkDYGelFnDBX>BKD|+L0&LpEeSmEZuGOG>$7d&!V{S7tZ7%*`+j4*zLTXPoRVl)r{c&!KkoM3Y^cf*rR zY%d#-iGE}fjuaI5h2&}ul02d*9J#(H7T=>GzocizKa+N9)r)qTaN&k@l79YdV)HR` zZiv|6>CK7Ebmu`KhDo$hfb$60KpkSZnJXdqcAwI>ymRhCE{zr$F+*Sa%sL?8usQq} zy&7F22$f;T>xE%IzXFvlmR0FTR4e6|p@6NTsa&~*9UsLPQ1fOkoX1O;ld~0KLX-(d zA&RVS=9M;#(8US8^_QY0eST*O)N&H_$Tq2A=GPfJ$Pod<2ksyhGJ zYgD{Dcf96(wzmTopV8%{C<8|;Bp`Fn4r=8oYrLYIBL1Oc*H0s8ZMsj;cN@-cBjSf$ zVqI3=8ebKP`M|JXz6-t}U7NKhbKoG%%p#i9#t#6bqUy1fc290tOR>_(2d2FL)bh~u zO<*{^$2nlj+Nanx#t{2Z97Q&P>o|q1L=FXtjHV%UZtp#*5+U*R6-6UW%p0nucyVJS z?ZS@V(Dy8sf%7uw_Okd%OT19$2BW4bbal|OeqQW5hyoV7}Z^JrM!F{CTcA$^BeUCNBWXW6a9r(;SZ^?rv-JWKgevj`9I?= zfC)O}9>^tHLBU+>n@x#h+@`vRha=qQZ>YW65$Rk4S$c=}y)+b~j1*igaIK4t3zhV{ z4t`gJ}2RdBRg>vakF9U(|Hg=;)PthU1jv7&X;C=Q;G?hnLXJ0TVwBKM2t`i#TO zS&VPiEiVgXj1>R*iT&Cp^Dtu;D#!JE6{YXFQ%i&Yg+>?y5696=lgA)Sy`+dd^`|lI=*Ylt4 zb`*7VLwv;m08ukvQUBc+N1;at_%~=IyFmW`I6oTWXgPh0p#l1z;yMaGs^T|rIQLOa T(MS;c4*&qMuL_%9_ZRP9@fu;D diff --git a/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231105.osk b/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231105.osk new file mode 100644 index 0000000000000000000000000000000000000000..16f89502eff0bc46df9922fec370ee1d804170f6 GIT binary patch literal 1899 zcmWIWW@Zs#U|`^2U=>IWU(2a$c^JquWCDpWFcfEJ=ILeTWuEQdo5y4*&{A!Gw1sPl z%y(f;r;?D8l}@c1Vtd%dzP&s7{PN3ry-NL4!aF>tXnFaYU#gL+{apE5!+TG>#M-L$ zizgfVX?3b5E|D`);hl1)bI*K-YnzT1HhnuR{6^sY4(_?fzVs-6-5KKe+J>9etx|rg zjG)EDr7qr^?{6wlsW-@<8S$Uztj4_1v=@t#?uGbnK47z=bZzo(gV~C)ChIm%Uj0Jj zn9UA@Z~Hg?IM(GODp|EM?)BA&2jBf!92Fiq*LK&-{&(U5C>}LoaOf%ndaxUad7&Q7 z%uCDH%PP*#Gwm_tI^-bW`g=~&Osm~FN$KihlhimKF^X;GbUrcPAW~59{krBG`~Lr{ z4m}q*f0|FVdpD0wkzCCs&j;b6_m7obKXPNo>^1(Y&e~=9xnAK5KQn>7ZqMN`A+F_z zn)nvaP`qtBXW!3)nUV=U4-?*Q?rOaCw|vz-c~l36?qoWA5f}^S6c`x9fDZIc%*^u$ zb#czmEy&MH%_}JeyY%hM`}wyF1diYT9qwY|?9y<_sI$E<@3o1qbM|iAkkBVHvL9W& z@#2lc;#L2D&vDQU@RyFAtT5|C!>7I7^|j~Ht#{fN%(m=?xLsCSyCL!Zy#JO{IqVZfoDx4sx10T08KL%XUT@v|qaQxInN8Sup`q1r zv(f8mHmUJ5Z)W{9ZYyX`i>b9#4B4#m+RJ+OjJ=PQ(w=cHH58g9;b^v#aV$GrthTgy>^{vX^<~pp_~0Hd#~SvR7WADnJ(QoPE5=_ zcC{e#jaOIQ~!qITd~rQOiRVTX)KldmQ%dB_Rh`6=7me;zH9BN zd~kQYbJfu|_A}p}Q}6wIc-yU}yZ@F2F1@wq=k)(wds_O#^O=kL&KSiwvu#P5_V|hq z`_1_q1E zzyL``1_onbM&Ji#g5dnT^x)K-)Z`Ly>d&3%pMBVX=ji*tBFCO5gqdwKdXbn?FJZlA z&0W1zcJ)x<;83n@hoijq{eCkcHIvhB#`nozzfABKR(!Jfs=bwS#ZukV)rDL-7nm$B zOMKaNb From 634795e45f12676c277be583bad61c29cc3ec22a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Nov 2023 02:50:14 +0300 Subject: [PATCH 041/207] Adjust failing test scenes --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 4e5db5d46e..ec7d92faac 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Begin drag top left", () => { - InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4)); + InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4, box1.ScreenSpaceDrawQuad.Height / 8)); InputManager.PressButton(MouseButton.Left); }); @@ -146,8 +146,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("Add big black box", () => { - InputManager.MoveMouseTo(skinEditor.ChildrenOfType().First()); - InputManager.Click(MouseButton.Left); + skinEditor.ChildrenOfType().First(b => b.ChildrenOfType().FirstOrDefault() != null).TriggerClick(); }); AddStep("store box", () => From 86fb33cb90b500dba57b2c199b381e7e8473b558 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 5 Nov 2023 12:40:06 +0100 Subject: [PATCH 042/207] Add disable taps checkbox to touch input settings --- osu.Android/OsuGameAndroid.cs | 4 +++ osu.Game/Configuration/OsuConfigManager.cs | 4 +++ osu.Game/Localisation/TouchSettingsStrings.cs | 24 ++++++++++++++ .../Settings/Sections/Input/TouchSettings.cs | 32 ++++++++++++------- 4 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Localisation/TouchSettingsStrings.cs diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index dea70e6b27..e4b934a387 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Handlers; using osu.Framework.Platform; using osu.Game; using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Updater; using osu.Game.Utils; @@ -97,6 +98,9 @@ namespace osu.Android case AndroidJoystickHandler jh: return new AndroidJoystickSettings(jh); + case AndroidTouchHandler: + return new TouchSettings(handler); + default: return base.CreateSettingsSubsectionFor(handler); } diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 5d2d782063..339817985e 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -330,6 +330,10 @@ namespace osu.Game.Configuration ShowHealthDisplayWhenCantFail, FadePlayfieldWhenHealthLow, + + /// + /// Disables mouse buttons clicks and touchscreen taps during gameplay. + /// MouseDisableButtons, MouseDisableWheel, ConfineMouseMode, diff --git a/osu.Game/Localisation/TouchSettingsStrings.cs b/osu.Game/Localisation/TouchSettingsStrings.cs new file mode 100644 index 0000000000..785b333100 --- /dev/null +++ b/osu.Game/Localisation/TouchSettingsStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class TouchSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.TouchSettings"; + + /// + /// "Touch" + /// + public static LocalisableString Touch => new TranslatableString(getKey(@"touch"), @"Touch"); + + /// + /// "Disable taps during gameplay" + /// + public static LocalisableString DisableTapsDuringGameplay => new TranslatableString(getKey(@"disable_taps_during_gameplay"), @"Disable taps during gameplay"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs index 8d1b12d5b2..b1b1b59429 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs @@ -3,38 +3,48 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Input.Handlers.Touch; +using osu.Framework.Input.Handlers; using osu.Framework.Localisation; +using osu.Game.Configuration; using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { + /// + /// Touch input settings subsection common to all touch handlers (even on different platforms). + /// public partial class TouchSettings : SettingsSubsection { - private readonly TouchHandler handler; + private readonly InputHandler handler; - public TouchSettings(TouchHandler handler) + protected override LocalisableString Header => TouchSettingsStrings.Touch; + + public TouchSettings(InputHandler handler) { this.handler = handler; } [BackgroundDependencyLoader] - private void load() + private void load(OsuConfigManager osuConfig) { - Children = new Drawable[] + if (!RuntimeInfo.IsMobile) // don't allow disabling the only input method (touch) on mobile. { - new SettingsCheckbox + Add(new SettingsCheckbox { LabelText = CommonStrings.Enabled, Current = handler.Enabled - }, - }; + }); + } + + Add(new SettingsCheckbox + { + LabelText = TouchSettingsStrings.DisableTapsDuringGameplay, + Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons) + }); } public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"touchscreen" }); - - protected override LocalisableString Header => handler.Description; } } From fa1d1df594d3f5307e01207952c4eb6be7ee3494 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 5 Nov 2023 12:43:14 +0100 Subject: [PATCH 043/207] Rename mouse button string to `Disable clicks during gameplay` --- osu.Android/AndroidMouseSettings.cs | 2 +- osu.Game/Localisation/MouseSettingsStrings.cs | 6 +++--- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 2 +- osu.Game/Screens/Play/PlayerSettings/InputSettings.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Android/AndroidMouseSettings.cs b/osu.Android/AndroidMouseSettings.cs index d6d7750448..fd01b11164 100644 --- a/osu.Android/AndroidMouseSettings.cs +++ b/osu.Android/AndroidMouseSettings.cs @@ -70,7 +70,7 @@ namespace osu.Android }, new SettingsCheckbox { - LabelText = MouseSettingsStrings.DisableMouseButtons, + LabelText = MouseSettingsStrings.DisableClicksDuringGameplay, Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons), }, }); diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index 1772f03b29..e61af07364 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -40,14 +40,14 @@ namespace osu.Game.Localisation public static LocalisableString DisableMouseWheelVolumeAdjust => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust"), @"Disable mouse wheel adjusting volume during gameplay"); /// - /// "Volume can still be adjusted using the mouse wheel by holding "Alt"" + /// "Volume can still be adjusted using the mouse wheel by holding "Alt"" /// public static LocalisableString DisableMouseWheelVolumeAdjustTooltip => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust_tooltip"), @"Volume can still be adjusted using the mouse wheel by holding ""Alt"""); /// - /// "Disable mouse buttons during gameplay" + /// "Disable clicks during gameplay" /// - public static LocalisableString DisableMouseButtons => new TranslatableString(getKey(@"disable_mouse_buttons"), @"Disable mouse buttons during gameplay"); + public static LocalisableString DisableClicksDuringGameplay => new TranslatableString(getKey(@"disable_clicks"), @"Disable clicks during gameplay"); /// /// "Enable high precision mouse to adjust sensitivity" diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index dfaeafbf5d..6bf06f4f98 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, new SettingsCheckbox { - LabelText = MouseSettingsStrings.DisableMouseButtons, + LabelText = MouseSettingsStrings.DisableClicksDuringGameplay, Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons) }, }; diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index cf261ba49b..4076782ee1 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { mouseButtonsCheckbox = new PlayerCheckbox { - LabelText = MouseSettingsStrings.DisableMouseButtons + LabelText = MouseSettingsStrings.DisableClicksDuringGameplay } }; } From 0d8bfedf5d3693809e76471b9feb47ebf140e40b Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 5 Nov 2023 12:44:22 +0100 Subject: [PATCH 044/207] Rename popup/binding string to `Toggle gameplay clicks/taps` --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 339817985e..e3f950ce2c 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -246,7 +246,7 @@ namespace osu.Game.Configuration ), new TrackedSetting(OsuSetting.MouseDisableButtons, disabledState => new SettingDescription( rawValue: !disabledState, - name: GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons, + name: GlobalActionKeyBindingStrings.ToggleGameplayClicksTaps, value: disabledState ? CommonStrings.Disabled.ToLower() : CommonStrings.Enabled.ToLower(), shortcut: LookupKeyBindings(GlobalAction.ToggleGameplayMouseButtons)) ), diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 947cd5f54f..b8163cc3b1 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -237,7 +237,7 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TakeScreenshot))] TakeScreenshot, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons))] + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleGameplayClicksTaps))] ToggleGameplayMouseButtons, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Back))] diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 8356c480dd..1bbbbdc3bc 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -70,9 +70,9 @@ namespace osu.Game.Localisation public static LocalisableString TakeScreenshot => new TranslatableString(getKey(@"take_screenshot"), @"Take screenshot"); /// - /// "Toggle gameplay mouse buttons" + /// "Toggle gameplay clicks/taps" /// - public static LocalisableString ToggleGameplayMouseButtons => new TranslatableString(getKey(@"toggle_gameplay_mouse_buttons"), @"Toggle gameplay mouse buttons"); + public static LocalisableString ToggleGameplayClicksTaps => new TranslatableString(getKey(@"toggle_gameplay_clicks_taps"), @"Toggle gameplay clicks/taps"); /// /// "Back" From 9947897c5f83c7298c381015a95673902486b52d Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 5 Nov 2023 12:53:40 +0100 Subject: [PATCH 045/207] Use appropriate clicks/taps text in player loader input settings --- osu.Game/Screens/Play/PlayerSettings/InputSettings.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index 4076782ee1..f6b0cddcf1 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.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.Graphics; using osu.Game.Configuration; @@ -19,7 +20,8 @@ namespace osu.Game.Screens.Play.PlayerSettings { mouseButtonsCheckbox = new PlayerCheckbox { - LabelText = MouseSettingsStrings.DisableClicksDuringGameplay + // TODO: change to touchscreen detection once https://github.com/ppy/osu/pull/25348 makes it in + LabelText = RuntimeInfo.IsDesktop ? MouseSettingsStrings.DisableClicksDuringGameplay : TouchSettingsStrings.DisableTapsDuringGameplay } }; } From 11bd801795f5a838cfc478e486586476fed2eb30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 07:44:25 +0100 Subject: [PATCH 046/207] Use more intelligent default for `TouchInputActive` --- osu.Game/Configuration/SessionStatics.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 0fc2076a7e..8f0a60b23d 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -3,6 +3,7 @@ #nullable disable +using osu.Framework; using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Online.API.Requests.Responses; @@ -25,7 +26,7 @@ namespace osu.Game.Configuration SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); - SetDefault(Static.TouchInputActive, false); + SetDefault(Static.TouchInputActive, RuntimeInfo.IsMobile); } /// From 3c72c5bccd21b4127ceaae9346ec04e6b059db9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 07:48:09 +0100 Subject: [PATCH 047/207] Steer touch input flag via bindable rather than config manager --- osu.Game/Input/TouchInputInterceptor.cs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game/Input/TouchInputInterceptor.cs b/osu.Game/Input/TouchInputInterceptor.cs index 4e6177c70c..b566113a2a 100644 --- a/osu.Game/Input/TouchInputInterceptor.cs +++ b/osu.Game/Input/TouchInputInterceptor.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Events; @@ -22,12 +23,17 @@ namespace osu.Game.Input { public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - [Resolved] - private SessionStatics statics { get; set; } = null!; + private readonly BindableBool touchInputActive = new BindableBool(); + + [BackgroundDependencyLoader] + private void load(SessionStatics statics) + { + statics.BindWith(Static.TouchInputActive, touchInputActive); + } protected override bool Handle(UIEvent e) { - bool touchInputWasActive = statics.Get(Static.TouchInputActive); + bool touchInputWasActive = touchInputActive.Value; switch (e) { @@ -36,7 +42,7 @@ namespace osu.Game.Input { if (touchInputWasActive) Logger.Log($@"Touch input deactivated due to received {e.GetType().ReadableName()}", LoggingTarget.Input); - statics.SetValue(Static.TouchInputActive, false); + touchInputActive.Value = false; } break; @@ -44,7 +50,7 @@ namespace osu.Game.Input case TouchEvent: if (!touchInputWasActive) Logger.Log($@"Touch input activated due to received {e.GetType().ReadableName()}", LoggingTarget.Input); - statics.SetValue(Static.TouchInputActive, true); + touchInputActive.Value = true; break; case KeyDownEvent keyDown: @@ -59,10 +65,8 @@ namespace osu.Game.Input [Conditional("TOUCH_INPUT_DEBUG")] private void debugToggleTouchInputActive() { - bool oldValue = statics.Get(Static.TouchInputActive); - bool newValue = !oldValue; - Logger.Log($@"Debug-toggling touch input to {(newValue ? @"active" : @"inactive")}", LoggingTarget.Input, LogLevel.Debug); - statics.SetValue(Static.TouchInputActive, newValue); + Logger.Log($@"Debug-toggling touch input to {(touchInputActive.Value ? @"inactive" : @"active")}", LoggingTarget.Input, LogLevel.Debug); + touchInputActive.Toggle(); } } } From adb9ca5a13cb920c6554457392a9f715b85038ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 07:56:09 +0100 Subject: [PATCH 048/207] Add failing test coverage for incorrect treatment of TD in mod presets --- .../UserInterface/TestSceneModPresetColumn.cs | 13 +++++++++++-- .../Visual/UserInterface/TestSceneModPresetPanel.cs | 8 ++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs index 1779b240cc..b7c1428397 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestAddingFlow() + public void TestAddingFlow([Values] bool withSystemModActive) { ModPresetColumn modPresetColumn = null!; @@ -181,7 +181,13 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("items loaded", () => modPresetColumn.IsLoaded && modPresetColumn.ItemsLoaded); AddAssert("add preset button disabled", () => !this.ChildrenOfType().Single().Enabled.Value); - AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModDaycore(), new OsuModClassic() }); + AddStep("set mods", () => + { + var newMods = new Mod[] { new OsuModDaycore(), new OsuModClassic() }; + if (withSystemModActive) + newMods = newMods.Append(new OsuModTouchDevice()).ToArray(); + SelectedMods.Value = newMods; + }); AddAssert("add preset button enabled", () => this.ChildrenOfType().Single().Enabled.Value); AddStep("click add preset button", () => @@ -209,6 +215,9 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("popover closed", () => !this.ChildrenOfType().Any()); AddUntilStep("preset creation occurred", () => this.ChildrenOfType().Count() == 4); + AddAssert("preset has correct mods", + () => this.ChildrenOfType().Single(panel => panel.Preset.Value.Name == "new preset").Preset.Value.Mods, + () => Has.Count.EqualTo(2)); AddStep("click add preset button", () => { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs index 35e352534b..c79cbd3691 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs @@ -86,6 +86,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("set mods to HD+HR+DT", () => SelectedMods.Value = new Mod[] { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() }); AddAssert("panel is not active", () => !panel.AsNonNull().Active.Value); + + // system mods are not included in presets. + AddStep("set mods to HR+DT+TD", () => SelectedMods.Value = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime(), new OsuModTouchDevice() }); + AddAssert("panel is active", () => panel.AsNonNull().Active.Value); } [Test] @@ -113,6 +117,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("set customised mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } }); AddStep("activate panel", () => panel.AsNonNull().TriggerClick()); assertSelectedModsEquivalentTo(new Mod[] { new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); + + AddStep("set system mod", () => SelectedMods.Value = new[] { new OsuModTouchDevice() }); + AddStep("activate panel", () => panel.AsNonNull().TriggerClick()); + assertSelectedModsEquivalentTo(new Mod[] { new OsuModTouchDevice(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); } private void assertSelectedModsEquivalentTo(IEnumerable mods) From 7ba07ab5305dc0be39bddc06967d826e36bfeea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 08:05:42 +0100 Subject: [PATCH 049/207] Add protections against handling system mods in mod presets --- osu.Game/Overlays/Mods/AddPresetPopover.cs | 2 +- osu.Game/Overlays/Mods/EditPresetPopover.cs | 4 ++-- osu.Game/Overlays/Mods/ModPresetPanel.cs | 14 +++++--------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Mods/AddPresetPopover.cs b/osu.Game/Overlays/Mods/AddPresetPopover.cs index 638592a9b5..b782b5d6ba 100644 --- a/osu.Game/Overlays/Mods/AddPresetPopover.cs +++ b/osu.Game/Overlays/Mods/AddPresetPopover.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Mods { Name = nameTextBox.Current.Value, Description = descriptionTextBox.Current.Value, - Mods = selectedMods.Value.ToArray(), + Mods = selectedMods.Value.Where(mod => mod.Type != ModType.System).ToArray(), Ruleset = r.Find(ruleset.Value.ShortName)! })); diff --git a/osu.Game/Overlays/Mods/EditPresetPopover.cs b/osu.Game/Overlays/Mods/EditPresetPopover.cs index 571021b0f8..8bce57c96a 100644 --- a/osu.Game/Overlays/Mods/EditPresetPopover.cs +++ b/osu.Game/Overlays/Mods/EditPresetPopover.cs @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Mods private void useCurrentMods() { - saveableMods = selectedMods.Value.ToHashSet(); + saveableMods = selectedMods.Value.Where(mod => mod.Type != ModType.System).ToHashSet(); updateState(); } @@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Mods if (!selectedMods.Value.Any()) return false; - return !saveableMods.SetEquals(selectedMods.Value); + return !saveableMods.SetEquals(selectedMods.Value.Where(mod => mod.Type != ModType.System)); } private void save() diff --git a/osu.Game/Overlays/Mods/ModPresetPanel.cs b/osu.Game/Overlays/Mods/ModPresetPanel.cs index 00f6e36972..3982abeba7 100644 --- a/osu.Game/Overlays/Mods/ModPresetPanel.cs +++ b/osu.Game/Overlays/Mods/ModPresetPanel.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.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -56,17 +55,14 @@ namespace osu.Game.Overlays.Mods protected override void Select() { - // if the preset is not active at the point of the user click, then set the mods using the preset directly, discarding any previous selections, - // which will also have the side effect of activating the preset (see `updateActiveState()`). - selectedMods.Value = Preset.Value.Mods.ToArray(); + var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System); + // will also have the side effect of activating the preset (see `updateActiveState()`). + selectedMods.Value = Preset.Value.Mods.Concat(selectedSystemMods).ToArray(); } protected override void Deselect() { - // if the preset is active when the user has clicked it, then it means that the set of active mods is exactly equal to the set of mods in the preset - // (there are no other active mods than what the preset specifies, and the mod settings match exactly). - // therefore it's safe to just clear selected mods, since it will have the effect of toggling the preset off. - selectedMods.Value = Array.Empty(); + selectedMods.Value = selectedMods.Value.Except(Preset.Value.Mods).ToArray(); } private void selectedModsChanged() @@ -79,7 +75,7 @@ namespace osu.Game.Overlays.Mods private void updateActiveState() { - Active.Value = new HashSet(Preset.Value.Mods).SetEquals(selectedMods.Value); + Active.Value = new HashSet(Preset.Value.Mods).SetEquals(selectedMods.Value.Where(mod => mod.Type != ModType.System)); } #region Filtering support From 40d081ee2de0940600f94ff126259b019622483b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Nov 2023 16:05:50 +0900 Subject: [PATCH 050/207] Add note about `Width` requirement in `UserGridPanel` --- osu.Game/Users/UserGridPanel.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Users/UserGridPanel.cs b/osu.Game/Users/UserGridPanel.cs index f4ec1475b1..aac2315b2f 100644 --- a/osu.Game/Users/UserGridPanel.cs +++ b/osu.Game/Users/UserGridPanel.cs @@ -10,6 +10,10 @@ using osuTK; namespace osu.Game.Users { + /// + /// A user "card", commonly used in a grid layout or in popovers. + /// Comes with a preset height, but width must be specified. + /// public partial class UserGridPanel : ExtendedUserPanel { private const int margin = 10; From b45d8c785cd662796c6098f72aadf589b99161e4 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Mon, 6 Nov 2023 08:38:34 +0100 Subject: [PATCH 051/207] fixed review findings --- .../Online/TestSceneUserClickableAvatar.cs | 26 +++++++++++++++++++ osu.Game/Users/Drawables/ClickableAvatar.cs | 21 +++++++++++++++ osu.Game/Users/Drawables/UpdateableAvatar.cs | 1 + 3 files changed, 48 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index 13f559ac09..a24581f7ed 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -78,6 +78,8 @@ namespace osu.Game.Tests.Visual.Online Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), }, }, + new ClickableAvatar(), + new ClickableAvatar(), }, }; }); @@ -120,6 +122,30 @@ namespace osu.Game.Tests.Visual.Online AddWaitStep("wait for tooltip to show", 5); AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click null user {4}. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 4) + return; + + InputManager.MoveMouseTo(targets[3]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click null user {5}. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 5) + return; + + InputManager.MoveMouseTo(targets[4]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); } } } diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index d6c6afba0b..0ed9f56cc7 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -1,12 +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; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osuTK; @@ -21,6 +24,24 @@ namespace osu.Game.Users.Drawables Width = 300 }; + public override LocalisableString TooltipText + { + get + { + if (!Enabled.Value) + return string.Empty; + + return ShowUsernameTooltip ? (user?.Username ?? string.Empty) : ContextMenuStrings.ViewProfile; + } + set => throw new NotSupportedException(); + } + + /// + /// By default, the tooltip will show "view profile" as avatars are usually displayed next to a username. + /// Setting this to true exposes the username via tooltip for special cases where this is not true. + /// + public bool ShowUsernameTooltip { get; set; } + private readonly APIUser? user; [Resolved] diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index 58b3646995..711e7ab799 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -75,6 +75,7 @@ namespace osu.Game.Users.Drawables return new ClickableAvatar(user) { RelativeSizeAxes = Axes.Both, + ShowUsernameTooltip = showUsernameTooltip, }; } else From b62811633f07820848ec960e228989f02e30f68c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Nov 2023 17:17:19 +0900 Subject: [PATCH 052/207] Add test coverage of touching and missing not enabled touch mod --- .../Mods/TestSceneOsuModTouchDevice.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index 5134265741..bcfa407684 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -104,6 +104,24 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods AddAssert("no toasts displayed", () => testOnScreenDisplay.ToastCount, () => Is.Zero); } + [Test] + public void TestTouchMiss() + { + // ensure mouse is active (and that it's not suppressed due to touches in previous tests) + AddStep("click mouse", () => InputManager.Click(MouseButton.Left)); + + AddUntilStep("wait until 200 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(200).Within(500)); + AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); + AddUntilStep("wait until 200", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(200)); + AddStep("touch playfield", () => + { + var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddAssert("touch device mod not activated", () => Player.Mods.Value, () => Has.No.InstanceOf()); + } + [Test] public void TestSecondObjectTouched() { From 4a70f2435c574b82829a5d2508ae29f7d2e3a2fb Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Mon, 6 Nov 2023 09:22:50 +0100 Subject: [PATCH 053/207] fixed showUsernameTooltip --- .../OnlinePlay/Components/ParticipantsList.cs | 2 +- .../DrawableRoomParticipantsList.cs | 2 +- osu.Game/Users/Drawables/ClickableAvatar.cs | 21 ------------------- osu.Game/Users/Drawables/UpdateableAvatar.cs | 6 +----- 4 files changed, 3 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs index 00f0889cc8..cb1a846d6c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.OnlinePlay.Components RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex(@"27252d"), }, - avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both }, + avatar = new UpdateableAvatar { RelativeSizeAxes = Axes.Both }, }; } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 06f9f35479..1814f5359f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -289,7 +289,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components set => avatar.User = value; } - private readonly UpdateableAvatar avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both }; + private readonly UpdateableAvatar avatar = new UpdateableAvatar { RelativeSizeAxes = Axes.Both }; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 0ed9f56cc7..d6c6afba0b 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -1,15 +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.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Game.Graphics.Containers; -using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osuTK; @@ -24,24 +21,6 @@ namespace osu.Game.Users.Drawables Width = 300 }; - public override LocalisableString TooltipText - { - get - { - if (!Enabled.Value) - return string.Empty; - - return ShowUsernameTooltip ? (user?.Username ?? string.Empty) : ContextMenuStrings.ViewProfile; - } - set => throw new NotSupportedException(); - } - - /// - /// By default, the tooltip will show "view profile" as avatars are usually displayed next to a username. - /// Setting this to true exposes the username via tooltip for special cases where this is not true. - /// - public bool ShowUsernameTooltip { get; set; } - private readonly APIUser? user; [Resolved] diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index 711e7ab799..3c72d7f7e0 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -46,7 +46,6 @@ namespace osu.Game.Users.Drawables protected override double LoadDelay => 200; private readonly bool isInteractive; - private readonly bool showUsernameTooltip; private readonly bool showGuestOnNull; /// @@ -54,12 +53,10 @@ namespace osu.Game.Users.Drawables /// /// The initial user to display. /// If set to true, hover/click sounds will play and clicking the avatar will open the user's profile. - /// Whether to show the username rather than "view profile" on the tooltip. (note: this only applies if is also true) /// Whether to show a default guest representation on null user (as opposed to nothing). - public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUsernameTooltip = false, bool showGuestOnNull = true) + public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showGuestOnNull = true) { this.isInteractive = isInteractive; - this.showUsernameTooltip = showUsernameTooltip; this.showGuestOnNull = showGuestOnNull; User = user; @@ -75,7 +72,6 @@ namespace osu.Game.Users.Drawables return new ClickableAvatar(user) { RelativeSizeAxes = Axes.Both, - ShowUsernameTooltip = showUsernameTooltip, }; } else From e2928cc6b96f816320f238e5f653bd75c5ed9ca3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Nov 2023 17:32:39 +0900 Subject: [PATCH 054/207] Fix incorrect assertion check targets (and flip assertion for miss case) --- .../Mods/TestSceneOsuModTouchDevice.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index bcfa407684..b77cc038c9 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods InputManager.BeginTouch(touch); InputManager.EndTouch(touch); }); - AddAssert("touch device mod not activated", () => Player.Mods.Value, () => Has.No.InstanceOf()); + AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); } [Test] @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods InputManager.MoveMouseTo(Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); InputManager.Click(MouseButton.Left); }); - AddAssert("touch device mod not activated", () => Player.Mods.Value, () => Has.No.InstanceOf()); + AddAssert("touch device mod not activated", () => Player.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); AddStep("speed back up", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 1); AddUntilStep("wait until 5000 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000).Within(500)); From 97fee6143c7b81d169452396a490b8293b521527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 10:08:19 +0100 Subject: [PATCH 055/207] Rename touch "input handlers" to detectors --- .../{PlayerTouchInputHandler.cs => PlayerTouchInputDetector.cs} | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- ...lectTouchInputHandler.cs => SongSelectTouchInputDetector.cs} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Screens/Play/{PlayerTouchInputHandler.cs => PlayerTouchInputDetector.cs} (97%) rename osu.Game/Screens/Select/{SongSelectTouchInputHandler.cs => SongSelectTouchInputDetector.cs} (96%) diff --git a/osu.Game/Screens/Play/PlayerTouchInputHandler.cs b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs similarity index 97% rename from osu.Game/Screens/Play/PlayerTouchInputHandler.cs rename to osu.Game/Screens/Play/PlayerTouchInputDetector.cs index a7d41de105..8bef24b66c 100644 --- a/osu.Game/Screens/Play/PlayerTouchInputHandler.cs +++ b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play { - public partial class PlayerTouchInputHandler : Component + public partial class PlayerTouchInputDetector : Component { [Resolved] private Player player { get; set; } = null!; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index e018b8dab3..30fecbe149 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Play return; } - AddInternal(new PlayerTouchInputHandler()); + AddInternal(new PlayerTouchInputDetector()); } protected override void LoadAsyncComplete() diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 74454713d1..03083672d5 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -279,7 +279,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, }, - new SongSelectTouchInputHandler() + new SongSelectTouchInputDetector() }); if (ShowFooter) diff --git a/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs b/osu.Game/Screens/Select/SongSelectTouchInputDetector.cs similarity index 96% rename from osu.Game/Screens/Select/SongSelectTouchInputHandler.cs rename to osu.Game/Screens/Select/SongSelectTouchInputDetector.cs index 973dc12e12..b726acb45f 100644 --- a/osu.Game/Screens/Select/SongSelectTouchInputHandler.cs +++ b/osu.Game/Screens/Select/SongSelectTouchInputDetector.cs @@ -13,7 +13,7 @@ using osu.Game.Utils; namespace osu.Game.Screens.Select { - public partial class SongSelectTouchInputHandler : Component + public partial class SongSelectTouchInputDetector : Component { [Resolved] private Bindable ruleset { get; set; } = null!; From 204cd541e2b8876089c619abd94e97baafb97b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 10:14:56 +0100 Subject: [PATCH 056/207] Use placeholder mod icon for touch device --- osu.Game/Rulesets/Mods/ModTouchDevice.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModTouchDevice.cs b/osu.Game/Rulesets/Mods/ModTouchDevice.cs index a5dfe5448c..b80b042f11 100644 --- a/osu.Game/Rulesets/Mods/ModTouchDevice.cs +++ b/osu.Game/Rulesets/Mods/ModTouchDevice.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { @@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mods { public sealed override string Name => "Touch Device"; public sealed override string Acronym => "TD"; + public sealed override IconUsage? Icon => OsuIcon.PlayStyleTouch; public sealed override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen."; public sealed override double ScoreMultiplier => 1; public sealed override ModType Type => ModType.System; From aa6f14b0247f2fe0768379b9a1ae03f75827ba86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Nov 2023 18:16:04 +0900 Subject: [PATCH 057/207] Fix spinner test hitting assertion when spinning too fast --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index ea57a6a1b5..9980e1a55f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -153,7 +154,7 @@ namespace osu.Game.Rulesets.Osu.Tests { base.Update(); if (auto) - RotationTracker.AddRotation((float)(Clock.ElapsedFrameTime * spinRate.Value)); + RotationTracker.AddRotation((float)Math.Min(180, Clock.ElapsedFrameTime * spinRate.Value)); } } } From 86cf0a36cfb842cf97f074988a28c9958e9b0b81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Nov 2023 18:16:31 +0900 Subject: [PATCH 058/207] Add test coverage of spinner with no bonus ticks --- .../TestSceneSpinner.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 9980e1a55f..77b16dd0c5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -37,6 +37,12 @@ namespace osu.Game.Rulesets.Osu.Tests AddSliderStep("Spin rate", 0.5, 5, 1, val => spinRate.Value = val); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Reset rate", () => spinRate.Value = 1); + } + [TestCase(true)] [TestCase(false)] public void TestVariousSpinners(bool autoplay) @@ -47,6 +53,36 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay))); } + [Test] + public void TestSpinnerNoBonus() + { + AddStep("Set high spin rate", () => spinRate.Value = 5); + + Spinner spinner; + + AddStep("add spinner", () => SetContents(_ => + { + spinner = new Spinner + { + StartTime = Time.Current, + EndTime = Time.Current + 750, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) + } + }; + + spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { OverallDifficulty = 0 }); + + return drawableSpinner = new TestDrawableSpinner(spinner, true, spinRate) + { + Anchor = Anchor.Centre, + Depth = depthIndex++, + Scale = new Vector2(0.75f) + }; + })); + } + [Test] public void TestSpinningSamplePitchShift() { From b219a371a93d82c3638df3b47f51e85d20f1fb94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Nov 2023 18:29:51 +0900 Subject: [PATCH 059/207] Move sample playback logic local to avoid edge case with no bonus ticks Can't see a better way of doing this. --- .../Objects/Drawables/DrawableSpinner.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index aa43532f65..e159d06a02 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -45,6 +45,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private const float spinning_sample_initial_frequency = 1.0f; private const float spinning_sample_modulated_base_frequency = 0.5f; + private SkinnableSound maxBonusSample; + /// /// The amount of bonus score gained from spinning after the required number of spins, for display purposes. /// @@ -109,6 +111,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables MinimumSampleVolume = MINIMUM_SAMPLE_VOLUME, Looping = true, Frequency = { Value = spinning_sample_initial_frequency } + }, + maxBonusSample = new SkinnableSound + { + MinimumSampleVolume = MINIMUM_SAMPLE_VOLUME, } }); @@ -128,6 +134,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.OnFree(); spinningSample.ClearSamples(); + maxBonusSample.ClearSamples(); } protected override void LoadSamples() @@ -136,6 +143,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables spinningSample.Samples = HitObject.CreateSpinningSamples().Cast().ToArray(); spinningSample.Frequency.Value = spinning_sample_initial_frequency; + + maxBonusSample.Samples = new ISampleInfo[] { HitObject.CreateHitSampleInfo("spinnerbonus") }; } private void updateSpinningSample(ValueChangedEvent tracking) @@ -157,6 +166,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.StopAllSamples(); spinningSample?.Stop(); + maxBonusSample?.Stop(); } protected override void AddNestedHitObject(DrawableHitObject hitObject) @@ -303,8 +313,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult; - private int lastMaxSamplePlayback; - private void updateBonusScore() { if (ticks.Count == 0) @@ -327,9 +335,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (tick == null) { // we still want to play a sound. this will probably be a new sound in the future, but for now let's continue playing the bonus sound. - // round robin to avoid hitting playback concurrency. - tick = ticks.OfType().Skip(lastMaxSamplePlayback++ % HitObject.MaximumBonusSpins).First(); - tick.PlaySamples(); + // TODO: this doesn't concurrency. i can't figure out how to make it concurrency. samples are bad and need a refactor. + maxBonusSample.Play(); } else tick.TriggerResult(true); From 92e4a8666def6e4bcc66d008ca0d206ca6c6cbd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Nov 2023 18:43:47 +0900 Subject: [PATCH 060/207] Add `spinnerbonus-max` support and fallback to `spinnerbonus` --- .../Objects/Drawables/DrawableSpinner.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index e159d06a02..c0c135d145 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -144,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables spinningSample.Samples = HitObject.CreateSpinningSamples().Cast().ToArray(); spinningSample.Frequency.Value = spinning_sample_initial_frequency; - maxBonusSample.Samples = new ISampleInfo[] { HitObject.CreateHitSampleInfo("spinnerbonus") }; + maxBonusSample.Samples = new ISampleInfo[] { new SpinnerBonusMaxSampleInfo(HitObject.CreateHitSampleInfo()) }; } private void updateSpinningSample(ValueChangedEvent tracking) @@ -344,5 +345,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables completedFullSpins.Value++; } } + + public class SpinnerBonusMaxSampleInfo : HitSampleInfo + { + public override IEnumerable LookupNames + { + get + { + foreach (string name in base.LookupNames) + yield return name; + + foreach (string name in base.LookupNames) + yield return name.Replace("-max", string.Empty); + } + } + + public SpinnerBonusMaxSampleInfo(HitSampleInfo sampleInfo) + : base("spinnerbonus-max", sampleInfo.Bank, sampleInfo.Suffix, sampleInfo.Volume) + + { + } + } } } From 682668ccf0f73bdbb98b800afcaff983399d391d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 10:54:32 +0100 Subject: [PATCH 061/207] Remove touch device toasts entirely --- .../Mods/TestSceneOsuModTouchDevice.cs | 21 ------------------- .../Overlays/OSD/TouchDeviceDetectedToast.cs | 15 ------------- .../Screens/Play/PlayerTouchInputDetector.cs | 12 ----------- 3 files changed, 48 deletions(-) delete mode 100644 osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index b77cc038c9..e41cc8cfbc 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -4,13 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Input; -using osu.Game.Overlays; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -25,8 +23,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods [Resolved] private SessionStatics statics { get; set; } = null!; - private TestOnScreenDisplay testOnScreenDisplay = null!; - protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => @@ -54,14 +50,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods [BackgroundDependencyLoader] private void load() { - Add(testOnScreenDisplay = new TestOnScreenDisplay()); Add(new TouchInputInterceptor()); - Dependencies.CacheAs(testOnScreenDisplay); } public override void SetUpSteps() { - AddStep("reset OSD toast count", () => testOnScreenDisplay.ToastCount = 0); AddStep("reset static", () => statics.SetValue(Static.TouchInputActive, false)); base.SetUpSteps(); } @@ -85,7 +78,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods InputManager.EndTouch(touch); }); AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); - AddAssert("no toasts displayed", () => testOnScreenDisplay.ToastCount, () => Is.Zero); } [Test] @@ -101,7 +93,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods InputManager.EndTouch(touch); }); AddAssert("touch device mod not activated", () => Player.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); - AddAssert("no toasts displayed", () => testOnScreenDisplay.ToastCount, () => Is.Zero); } [Test] @@ -149,18 +140,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods InputManager.EndTouch(touch); }); AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); - AddAssert("toast displayed", () => testOnScreenDisplay.ToastCount, () => Is.EqualTo(1)); - } - - private partial class TestOnScreenDisplay : OnScreenDisplay - { - public int ToastCount { get; set; } - - protected override void DisplayTemporarily(Drawable toDisplay) - { - base.DisplayTemporarily(toDisplay); - ToastCount++; - } } } } diff --git a/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs b/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs deleted file mode 100644 index 266e10ab1f..0000000000 --- a/osu.Game/Overlays/OSD/TouchDeviceDetectedToast.cs +++ /dev/null @@ -1,15 +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.Game.Rulesets; - -namespace osu.Game.Overlays.OSD -{ - public partial class TouchDeviceDetectedToast : Toast - { - public TouchDeviceDetectedToast(RulesetInfo ruleset) - : base(ruleset.Name, "Touch device detected", "Touch Device mod applied to score") - { - } - } -} diff --git a/osu.Game/Screens/Play/PlayerTouchInputDetector.cs b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs index 8bef24b66c..a5055dfb00 100644 --- a/osu.Game/Screens/Play/PlayerTouchInputDetector.cs +++ b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs @@ -6,10 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; -using osu.Game.Overlays; -using osu.Game.Overlays.OSD; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play { @@ -21,9 +18,6 @@ namespace osu.Game.Screens.Play [Resolved] private GameplayState gameplayState { get; set; } = null!; - [Resolved] - private OnScreenDisplay? onScreenDisplay { get; set; } - private IBindable touchActive = new BindableBool(); [BackgroundDependencyLoader] @@ -51,12 +45,6 @@ namespace osu.Game.Screens.Play if (touchDeviceMod == null) return; - // do not show the toast if the user hasn't hit anything yet. - // we're kind of assuming that the user just switches to touch for gameplay - // and we don't want to spam them with obvious toasts. - if (gameplayState.ScoreProcessor.HitEvents.Any(ev => ev.Result.IsHit())) - onScreenDisplay?.Display(new TouchDeviceDetectedToast(gameplayState.Ruleset.RulesetInfo)); - // `Player` (probably rightly so) assumes immutability of mods, // so this will not be shown immediately on the mod display in the top right. // if this is to change, the mod immutability should be revisited. From 718492a0b7d9c38b32102095c84393fbe5a792d9 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Mon, 6 Nov 2023 11:29:15 +0100 Subject: [PATCH 062/207] fixed DRY --- .../Online/TestSceneUserClickableAvatar.cs | 83 +++++++------------ 1 file changed, 32 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index a24581f7ed..72870a5647 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -27,59 +27,14 @@ namespace osu.Game.Tests.Visual.Online Anchor = Anchor.Centre, Origin = Anchor.Centre, Spacing = new Vector2(10f), - Children = new Drawable[] + Children = new[] { - new ClickableAvatar(new APIUser - { - Username = @"flyte", Id = 3103765, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - }) - { - Width = 50, - Height = 50, - CornerRadius = 10, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), - }, - }, - new ClickableAvatar(new APIUser - { - Username = @"peppy", Id = 2, Colour = "99EB47", CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", - }) - { - Width = 50, - Height = 50, - CornerRadius = 10, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), - }, - }, - new ClickableAvatar(new APIUser - { - Username = @"flyte", - Id = 3103765, - CountryCode = CountryCode.JP, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", - Status = - { - Value = new UserStatusOnline() - } - }) - { - Width = 50, - Height = 50, - CornerRadius = 10, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), - }, - }, - new ClickableAvatar(), + generateUser(@"peppy", 2, CountryCode.AU, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", "99EB47"), + generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"), + generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"), new ClickableAvatar(), + new UpdateableAvatar(), + new UpdateableAvatar(), }, }; }); @@ -147,5 +102,31 @@ namespace osu.Game.Tests.Visual.Online AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); AddWaitStep("wait for tooltip to hide", 3); } + + private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, string? color = null) + { + return new ClickableAvatar(new APIUser + { + Username = username, + Id = id, + CountryCode = countryCode, + CoverUrl = cover, + Colour = color ?? "000000", + Status = + { + Value = new UserStatusOnline() + } + }) + { + Width = 50, + Height = 50, + CornerRadius = 10, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), + }, + }; + } } } From 39ad91feea58e0a91e95b3a2be0889ff98be4ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Nov 2023 11:50:04 +0100 Subject: [PATCH 063/207] Make debug input toggle post notifications --- osu.Game/Input/TouchInputInterceptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/TouchInputInterceptor.cs b/osu.Game/Input/TouchInputInterceptor.cs index b566113a2a..368d8469ae 100644 --- a/osu.Game/Input/TouchInputInterceptor.cs +++ b/osu.Game/Input/TouchInputInterceptor.cs @@ -65,7 +65,7 @@ namespace osu.Game.Input [Conditional("TOUCH_INPUT_DEBUG")] private void debugToggleTouchInputActive() { - Logger.Log($@"Debug-toggling touch input to {(touchInputActive.Value ? @"inactive" : @"active")}", LoggingTarget.Input, LogLevel.Debug); + Logger.Log($@"Debug-toggling touch input to {(touchInputActive.Value ? @"inactive" : @"active")}", LoggingTarget.Information, LogLevel.Important); touchInputActive.Toggle(); } } From 034f53da4b1d79220d8af95055a203076ab88c87 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Mon, 6 Nov 2023 11:54:57 +0100 Subject: [PATCH 064/207] added isEnabled to tooltip --- osu.Game/Users/Drawables/ClickableAvatar.cs | 16 +++++++++++++++- osu.Game/Users/Drawables/UpdateableAvatar.cs | 5 ++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index d6c6afba0b..0520f62665 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -14,13 +14,15 @@ namespace osu.Game.Users.Drawables { public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new UserGridPanelTooltip(); + public ITooltip GetCustomTooltip() => new UserGridPanelTooltip(IsTooltipEnabled); public UserGridPanel TooltipContent => new UserGridPanel(user!) { Width = 300 }; + public bool IsTooltipEnabled; + private readonly APIUser? user; [Resolved] @@ -33,6 +35,7 @@ namespace osu.Game.Users.Drawables public ClickableAvatar(APIUser? user = null) { this.user = user; + IsTooltipEnabled = true; if (user?.Id != APIUser.SYSTEM_USER_ID) Action = openProfile; @@ -60,10 +63,21 @@ namespace osu.Game.Users.Drawables private partial class UserGridPanelTooltip : VisibilityContainer, ITooltip { + private readonly bool isEnabled; private UserGridPanel? displayedUser; + public UserGridPanelTooltip(bool isEnabled = true) + { + this.isEnabled = isEnabled; + } + protected override void PopIn() { + if (displayedUser is null || !isEnabled) + { + return; + } + Child = displayedUser; this.FadeIn(20, Easing.OutQuint); } diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index 3c72d7f7e0..a970997056 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -47,17 +47,20 @@ namespace osu.Game.Users.Drawables private readonly bool isInteractive; private readonly bool showGuestOnNull; + private readonly bool showUserPanel; /// /// Construct a new UpdateableAvatar. /// /// The initial user to display. /// If set to true, hover/click sounds will play and clicking the avatar will open the user's profile. + /// If set to true, the user status panel will be displayed in the tooltip. /// Whether to show a default guest representation on null user (as opposed to nothing). - public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showGuestOnNull = true) + public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUserPanel = true, bool showGuestOnNull = true) { this.isInteractive = isInteractive; this.showGuestOnNull = showGuestOnNull; + this.showUserPanel = showUserPanel; User = user; } From a01f6187f4738f85376cb820315d95d8c4545e4c Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Mon, 6 Nov 2023 14:52:06 +0100 Subject: [PATCH 065/207] testing the tooltip --- .../Online/TestSceneUserClickableAvatar.cs | 134 +++++++++--------- osu.Game/Users/Drawables/ClickableAvatar.cs | 43 +++++- osu.Game/Users/Drawables/UpdateableAvatar.cs | 1 + 3 files changed, 102 insertions(+), 76 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index 72870a5647..678767f15e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -1,13 +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 System.Linq; using NUnit.Framework; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; using osu.Game.Users.Drawables; @@ -29,12 +27,9 @@ namespace osu.Game.Tests.Visual.Online Spacing = new Vector2(10f), Children = new[] { - generateUser(@"peppy", 2, CountryCode.AU, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", "99EB47"), - generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"), - generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"), - new ClickableAvatar(), - new UpdateableAvatar(), - new UpdateableAvatar(), + generateUser(@"peppy", 2, CountryCode.AU, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", false, "99EB47"), + generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", false), + generateUser(@"joshika39", 17032217, CountryCode.RS, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", true), }, }; }); @@ -42,68 +37,68 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestClickableAvatarHover() { - AddStep($"click {1}. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 1) - return; - - InputManager.MoveMouseTo(targets[0]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click {2}. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 2) - return; - - InputManager.MoveMouseTo(targets[1]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click {3}. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 3) - return; - - InputManager.MoveMouseTo(targets[2]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click null user {4}. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 4) - return; - - InputManager.MoveMouseTo(targets[3]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click null user {5}. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 5) - return; - - InputManager.MoveMouseTo(targets[4]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); + // AddStep($"click {1}. {nameof(ClickableAvatar)}", () => + // { + // var targets = this.ChildrenOfType().ToList(); + // if (targets.Count < 1) + // return; + // + // InputManager.MoveMouseTo(targets[0]); + // }); + // AddWaitStep("wait for tooltip to show", 5); + // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + // AddWaitStep("wait for tooltip to hide", 3); + // + // AddStep($"click {2}. {nameof(ClickableAvatar)}", () => + // { + // var targets = this.ChildrenOfType().ToList(); + // if (targets.Count < 2) + // return; + // + // InputManager.MoveMouseTo(targets[1]); + // }); + // AddWaitStep("wait for tooltip to show", 5); + // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + // AddWaitStep("wait for tooltip to hide", 3); + // + // AddStep($"click {3}. {nameof(ClickableAvatar)}", () => + // { + // var targets = this.ChildrenOfType().ToList(); + // if (targets.Count < 3) + // return; + // + // InputManager.MoveMouseTo(targets[2]); + // }); + // AddWaitStep("wait for tooltip to show", 5); + // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + // AddWaitStep("wait for tooltip to hide", 3); + // + // AddStep($"click null user {4}. {nameof(ClickableAvatar)}", () => + // { + // var targets = this.ChildrenOfType().ToList(); + // if (targets.Count < 4) + // return; + // + // InputManager.MoveMouseTo(targets[3]); + // }); + // AddWaitStep("wait for tooltip to show", 5); + // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + // AddWaitStep("wait for tooltip to hide", 3); + // + // AddStep($"click null user {5}. {nameof(ClickableAvatar)}", () => + // { + // var targets = this.ChildrenOfType().ToList(); + // if (targets.Count < 5) + // return; + // + // InputManager.MoveMouseTo(targets[4]); + // }); + // AddWaitStep("wait for tooltip to show", 5); + // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + // AddWaitStep("wait for tooltip to hide", 3); } - private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, string? color = null) + private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool isTooltipEnabled, string? color = null) { return new ClickableAvatar(new APIUser { @@ -115,7 +110,7 @@ namespace osu.Game.Tests.Visual.Online Status = { Value = new UserStatusOnline() - } + }, }) { Width = 50, @@ -126,6 +121,7 @@ namespace osu.Game.Tests.Visual.Online { Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), }, + IsTooltipEnabled = isTooltipEnabled, }; } } diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 0520f62665..c11ad7f720 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -1,12 +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; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osuTK; @@ -14,14 +17,32 @@ namespace osu.Game.Users.Drawables { public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new UserGridPanelTooltip(IsTooltipEnabled); + public ITooltip GetCustomTooltip() => new UserGridPanelTooltip(this); public UserGridPanel TooltipContent => new UserGridPanel(user!) { Width = 300 }; - public bool IsTooltipEnabled; + public override LocalisableString TooltipText + { + get + { + if (!Enabled.Value) + return string.Empty; + + return !IsTooltipEnabled ? (user?.Username ?? string.Empty) : ContextMenuStrings.ViewProfile; + } + set => throw new NotSupportedException(); + } + + /// + /// By default, the tooltip will show "view profile" as avatars are usually displayed next to a username. + /// Setting this to true exposes the username via tooltip for special cases where this is not true. + /// + // public bool ShowUsernameTooltip { get; set; } + + public bool IsTooltipEnabled { get; set; } private readonly APIUser? user; @@ -35,12 +56,16 @@ namespace osu.Game.Users.Drawables public ClickableAvatar(APIUser? user = null) { this.user = user; - IsTooltipEnabled = true; if (user?.Id != APIUser.SYSTEM_USER_ID) Action = openProfile; } + public void SetValue(out bool value) + { + value = IsTooltipEnabled; + } + [BackgroundDependencyLoader] private void load() { @@ -61,18 +86,22 @@ namespace osu.Game.Users.Drawables return base.OnClick(e); } - private partial class UserGridPanelTooltip : VisibilityContainer, ITooltip + public partial class UserGridPanelTooltip : VisibilityContainer, ITooltip { - private readonly bool isEnabled; + private readonly ClickableAvatar parent; private UserGridPanel? displayedUser; + private bool isEnabled; - public UserGridPanelTooltip(bool isEnabled = true) + public UserGridPanelTooltip(ClickableAvatar parent) { - this.isEnabled = isEnabled; + this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); + isEnabled = this.parent.IsTooltipEnabled; } protected override void PopIn() { + parent.SetValue(out isEnabled); + if (displayedUser is null || !isEnabled) { return; diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index a970997056..64d64c56ce 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -75,6 +75,7 @@ namespace osu.Game.Users.Drawables return new ClickableAvatar(user) { RelativeSizeAxes = Axes.Both, + IsTooltipEnabled = showUserPanel }; } else From f897c21b3f5b57b5892f25cef3644aabf82d7c43 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Mon, 6 Nov 2023 15:25:12 +0100 Subject: [PATCH 066/207] partial change --- osu.Game/Users/Drawables/ClickableAvatar.cs | 39 ++++++++------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index c11ad7f720..376ce0b821 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -15,14 +15,14 @@ using osuTK; namespace osu.Game.Users.Drawables { - public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip + public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new UserGridPanelTooltip(this); - - public UserGridPanel TooltipContent => new UserGridPanel(user!) + public ITooltip GetCustomTooltip() { - Width = 300 - }; + return new APIUserTooltip(user); + } + + public APIUser? TooltipContent => user; public override LocalisableString TooltipText { @@ -36,12 +36,6 @@ namespace osu.Game.Users.Drawables set => throw new NotSupportedException(); } - /// - /// By default, the tooltip will show "view profile" as avatars are usually displayed next to a username. - /// Setting this to true exposes the username via tooltip for special cases where this is not true. - /// - // public bool ShowUsernameTooltip { get; set; } - public bool IsTooltipEnabled { get; set; } private readonly APIUser? user; @@ -86,28 +80,23 @@ namespace osu.Game.Users.Drawables return base.OnClick(e); } - public partial class UserGridPanelTooltip : VisibilityContainer, ITooltip + public partial class APIUserTooltip : VisibilityContainer, ITooltip { - private readonly ClickableAvatar parent; - private UserGridPanel? displayedUser; - private bool isEnabled; + private APIUser? user; - public UserGridPanelTooltip(ClickableAvatar parent) + public APIUserTooltip(APIUser? user) { - this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); - isEnabled = this.parent.IsTooltipEnabled; + this.user = user; } protected override void PopIn() { - parent.SetValue(out isEnabled); - - if (displayedUser is null || !isEnabled) + if (user is null) { return; } - Child = displayedUser; + Child = new UserGridPanel(user); this.FadeIn(20, Easing.OutQuint); } @@ -115,9 +104,9 @@ namespace osu.Game.Users.Drawables public void Move(Vector2 pos) => Position = pos; - public void SetContent(UserGridPanel userGridPanel) + public void SetContent(APIUser user) { - displayedUser = userGridPanel; + this.user = user; } } } From 9d10d93085cd3cde44d63836911bc607d5618901 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 6 Nov 2023 20:45:52 +0300 Subject: [PATCH 067/207] Adjust test scene to see graph flickering --- .../Visual/Online/TestSceneGraph.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneGraph.cs index 4f19003638..eee29e0aeb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneGraph.cs @@ -13,22 +13,20 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public partial class TestSceneGraph : OsuTestScene { + private readonly BarGraph graph; + public TestSceneGraph() { - BarGraph graph; - - Children = new[] + Child = graph = new BarGraph { - graph = new BarGraph - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(0.5f), - }, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.5f), }; AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i)); + AddStep("small values", () => graph.Values = Enumerable.Range(1, 10).Select(i => i * 0.01f).Concat(new[] { 100f })); AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i)); AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i)); AddStep("empty values", () => graph.Values = Array.Empty()); @@ -37,5 +35,12 @@ namespace osu.Game.Tests.Visual.Online AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight); AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft); } + + protected override void LoadComplete() + { + base.LoadComplete(); + + graph.MoveToY(-10, 1000).Then().MoveToY(10, 1000).Loop(); + } } } From 944fee56f8d8e61895882a5c55c9d6ff0e6c637e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 6 Nov 2023 21:48:47 +0300 Subject: [PATCH 068/207] Add failing test case --- .../TestSceneBeatmapEditorNavigation.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index d0fa5fc737..d9757d8584 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -6,11 +6,15 @@ using NUnit.Framework; using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; @@ -232,6 +236,35 @@ namespace osu.Game.Tests.Visual.Navigation () => Is.EqualTo(beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0).ID)); } + [Test] + public void TestCreateNewDifficultyOnNonExistentBeatmap() + { + AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType().SingleOrDefault() != null); + + AddStep("open editor", () => Game.ChildrenOfType().Single().OnEdit.Invoke()); + AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded); + AddStep("click on file", () => + { + var item = getEditor().ChildrenOfType().Single(i => i.Item.Text.Value.ToString() == "File"); + item.TriggerClick(); + }); + AddStep("click on create new difficulty", () => + { + var item = getEditor().ChildrenOfType().Single(i => i.Item.Text.Value.ToString() == "Create new difficulty"); + item.TriggerClick(); + }); + AddStep("click on catch", () => + { + var item = getEditor().ChildrenOfType().Single(i => i.Item.Text.Value.ToString() == "osu!catch"); + item.TriggerClick(); + }); + AddAssert("save dialog displayed", () => Game.ChildrenOfType().Single().CurrentDialog is PromptForSaveDialog); + + AddStep("press forget all changes", () => Game.ChildrenOfType().Single().CurrentDialog!.PerformAction()); + AddWaitStep("wait", 5); + AddAssert("editor beatmap uses catch ruleset", () => getEditorBeatmap().BeatmapInfo.Ruleset.ShortName == "fruits"); + } + private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType().Single(); private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen; From b2749943e27ae31fb392b1bbad918cbd9432412e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 6 Nov 2023 21:52:46 +0300 Subject: [PATCH 069/207] Display "required save" popup when creating another difficulty on a new beatmap --- osu.Game/Screens/Edit/Editor.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 91c3c98f01..3136faf855 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1095,6 +1095,19 @@ namespace osu.Game.Screens.Edit protected void CreateNewDifficulty(RulesetInfo rulesetInfo) { + if (isNewBeatmap) + { + dialogOverlay.Push(new SaveRequiredPopupDialog("This beatmap will be saved in order to create another difficulty.", () => + { + if (!Save()) + return; + + CreateNewDifficulty(rulesetInfo); + })); + + return; + } + if (!rulesetInfo.Equals(editorBeatmap.BeatmapInfo.Ruleset)) { switchToNewDifficulty(rulesetInfo, false); From 38d16f620cdef1ab457ce2b82c5c9d1d91113761 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 6 Nov 2023 21:55:00 +0300 Subject: [PATCH 070/207] Alter test case to comply with new behaviour --- .../Visual/Navigation/TestSceneBeatmapEditorNavigation.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index d9757d8584..b79b61202b 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -12,9 +12,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; -using osu.Game.Overlays.Dialog; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; @@ -258,10 +256,10 @@ namespace osu.Game.Tests.Visual.Navigation var item = getEditor().ChildrenOfType().Single(i => i.Item.Text.Value.ToString() == "osu!catch"); item.TriggerClick(); }); - AddAssert("save dialog displayed", () => Game.ChildrenOfType().Single().CurrentDialog is PromptForSaveDialog); + AddAssert("save dialog displayed", () => Game.ChildrenOfType().Single().CurrentDialog is SaveRequiredPopupDialog); - AddStep("press forget all changes", () => Game.ChildrenOfType().Single().CurrentDialog!.PerformAction()); - AddWaitStep("wait", 5); + AddStep("press save", () => Game.ChildrenOfType().Single().CurrentDialog!.PerformOkAction()); + AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded); AddAssert("editor beatmap uses catch ruleset", () => getEditorBeatmap().BeatmapInfo.Ruleset.ShortName == "fruits"); } From d6e7145e1c676ff45eb820f42375500cba43c3a1 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 6 Nov 2023 20:42:40 +0100 Subject: [PATCH 071/207] Add new setting for GameplayDisableTaps --- osu.Game/Configuration/OsuConfigManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e3f950ce2c..21079fc092 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -108,6 +108,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.MouseDisableWheel, false); SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); + SetDefault(OsuSetting.GameplayDisableTaps, false); + // Graphics SetDefault(OsuSetting.ShowFpsDisplay, false); @@ -332,7 +334,7 @@ namespace osu.Game.Configuration FadePlayfieldWhenHealthLow, /// - /// Disables mouse buttons clicks and touchscreen taps during gameplay. + /// Disables mouse buttons clicks during gameplay. /// MouseDisableButtons, MouseDisableWheel, @@ -412,6 +414,7 @@ namespace osu.Game.Configuration EditorLimitedDistanceSnap, ReplaySettingsOverlay, AutomaticallyDownloadMissingBeatmaps, - EditorShowSpeedChanges + EditorShowSpeedChanges, + GameplayDisableTaps, } } From c1967a5cbb640cf296832efec00b392922c398c9 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 6 Nov 2023 20:43:24 +0100 Subject: [PATCH 072/207] Make tests fail --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 2e62689e2c..19340aac15 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -133,8 +133,11 @@ namespace osu.Game.Rulesets.Osu.Tests } [Test] - public void TestSimpleInput() + public void TestSimpleInput([Values] bool disableMouseButtons) { + // OsuSetting.MouseDisableButtons should not affect touch taps + AddStep($"{(disableMouseButtons ? "disable" : "enable")} mouse buttons", () => config.SetValue(OsuSetting.MouseDisableButtons, disableMouseButtons)); + beginTouch(TouchSource.Touch1); assertKeyCounter(1, 0); @@ -468,7 +471,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestInputWhileMouseButtonsDisabled() { - AddStep("Disable mouse buttons", () => config.SetValue(OsuSetting.MouseDisableButtons, true)); + AddStep("Disable gameplay taps", () => config.SetValue(OsuSetting.GameplayDisableTaps, true)); beginTouch(TouchSource.Touch1); @@ -620,6 +623,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("Release all touches", () => { config.SetValue(OsuSetting.MouseDisableButtons, false); + config.SetValue(OsuSetting.GameplayDisableTaps, false); foreach (TouchSource source in InputManager.CurrentState.Touch.ActiveSources) InputManager.EndTouch(new Touch(source, osuInputManager.ScreenSpaceDrawQuad.Centre)); }); From ea357bafddd9970fb2c7f06686e1604b6caca76f Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 6 Nov 2023 20:53:22 +0100 Subject: [PATCH 073/207] Fix tests by using the correct setting for touch input --- osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs | 8 +++----- osu.Game/Rulesets/UI/RulesetInputManager.cs | 6 ++++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs index 5277a1f7d6..994ec024b1 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI private readonly OsuInputManager osuInputManager; - private Bindable mouseDisabled = null!; + private Bindable tapsDisabled = null!; public OsuTouchInputMapper(OsuInputManager inputManager) { @@ -43,9 +43,7 @@ namespace osu.Game.Rulesets.Osu.UI [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - // The mouse button disable setting affects touch. It's a bit weird. - // This is mostly just doing the same as what is done in RulesetInputManager to match behaviour. - mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); + tapsDisabled = config.GetBindable(OsuSetting.GameplayDisableTaps); } // Required to handle touches outside of the playfield when screen scaling is enabled. @@ -64,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.UI : OsuAction.LeftButton; // Ignore any taps which trigger an action which is already handled. But track them for potential positional input in the future. - bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !mouseDisabled.Value && trackedTouches.All(t => t.Action != action); + bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !tapsDisabled.Value && trackedTouches.All(t => t.Action != action); // If we can actually accept as an action, check whether this tap was on a circle's receptor. // This case gets special handling to allow for empty-space stream tapping. diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 39b83ecca1..eb19368fc8 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -72,6 +72,7 @@ namespace osu.Game.Rulesets.UI private void load(OsuConfigManager config) { mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); + tapsDisabled = config.GetBindable(OsuSetting.GameplayDisableTaps); } #region Action mapping (for replays) @@ -124,6 +125,7 @@ namespace osu.Game.Rulesets.UI #region Setting application (disables etc.) private Bindable mouseDisabled; + private Bindable tapsDisabled; protected override bool Handle(UIEvent e) { @@ -147,9 +149,9 @@ namespace osu.Game.Rulesets.UI protected override bool HandleMouseTouchStateChange(TouchStateChangeEvent e) { - if (mouseDisabled.Value) + if (tapsDisabled.Value) { - // Only propagate positional data when mouse buttons are disabled. + // Only propagate positional data when taps are disabled. e = new TouchStateChangeEvent(e.State, e.Input, e.Touch, false, e.LastPosition); } From f8b5ecc92a2e303a87a23c218a24c103881cb8c5 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 6 Nov 2023 21:07:15 +0100 Subject: [PATCH 074/207] Update UI to use the new setting --- osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs | 2 +- osu.Game/Screens/Play/PlayerSettings/InputSettings.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs index b1b1b59429..793b707bfc 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Add(new SettingsCheckbox { LabelText = TouchSettingsStrings.DisableTapsDuringGameplay, - Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons) + Current = osuConfig.GetBindable(OsuSetting.GameplayDisableTaps) }); } diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index f6b0cddcf1..96b543d176 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs @@ -27,6 +27,6 @@ namespace osu.Game.Screens.Play.PlayerSettings } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) => mouseButtonsCheckbox.Current = config.GetBindable(OsuSetting.MouseDisableButtons); + private void load(OsuConfigManager config) => mouseButtonsCheckbox.Current = config.GetBindable(RuntimeInfo.IsDesktop ? OsuSetting.MouseDisableButtons : OsuSetting.GameplayDisableTaps); } } From a4ac50cf86f8cbd148eedf75b765c65ad34301b6 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 6 Nov 2023 21:10:04 +0100 Subject: [PATCH 075/207] Revert "Rename popup/binding string to `Toggle gameplay clicks/taps`" This reverts commit 0d8bfedf5d3693809e76471b9feb47ebf140e40b. --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 21079fc092..c44a089c49 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -248,7 +248,7 @@ namespace osu.Game.Configuration ), new TrackedSetting(OsuSetting.MouseDisableButtons, disabledState => new SettingDescription( rawValue: !disabledState, - name: GlobalActionKeyBindingStrings.ToggleGameplayClicksTaps, + name: GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons, value: disabledState ? CommonStrings.Disabled.ToLower() : CommonStrings.Enabled.ToLower(), shortcut: LookupKeyBindings(GlobalAction.ToggleGameplayMouseButtons)) ), diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b8163cc3b1..947cd5f54f 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -237,7 +237,7 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TakeScreenshot))] TakeScreenshot, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleGameplayClicksTaps))] + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons))] ToggleGameplayMouseButtons, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Back))] diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 1bbbbdc3bc..8356c480dd 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -70,9 +70,9 @@ namespace osu.Game.Localisation public static LocalisableString TakeScreenshot => new TranslatableString(getKey(@"take_screenshot"), @"Take screenshot"); /// - /// "Toggle gameplay clicks/taps" + /// "Toggle gameplay mouse buttons" /// - public static LocalisableString ToggleGameplayClicksTaps => new TranslatableString(getKey(@"toggle_gameplay_clicks_taps"), @"Toggle gameplay clicks/taps"); + public static LocalisableString ToggleGameplayMouseButtons => new TranslatableString(getKey(@"toggle_gameplay_mouse_buttons"), @"Toggle gameplay mouse buttons"); /// /// "Back" From 01e59d134a9b8436404a1c6de4bc5cb07332dfe8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 7 Nov 2023 00:48:51 +0300 Subject: [PATCH 076/207] Adjust health bar settings on default components initialiser to match new layout --- osu.Game/Skinning/ArgonSkin.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 3262812d24..6c4074fd92 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -128,9 +128,11 @@ namespace osu.Game.Skinning // elements default to beneath the health bar const float components_x_offset = 50; - health.Anchor = Anchor.TopCentre; - health.Origin = Anchor.TopCentre; - health.Y = 15; + health.Anchor = Anchor.TopLeft; + health.Origin = Anchor.TopLeft; + health.BarLength.Value = 0.22f; + health.BarHeight.Value = 30f; + health.Position = new Vector2(components_x_offset, 20f); if (scoreWedge != null) { From 754e05213c452f1f1ddc1c3485ae9e4270b23979 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 7 Nov 2023 00:53:20 +0300 Subject: [PATCH 077/207] Update argon score wedge design --- osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs | 43 -------------------- osu.Game/Skinning/ArgonSkin.cs | 2 +- 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs b/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs index dc0130fb8e..fe495b421f 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs @@ -5,22 +5,13 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { public partial class ArgonScoreWedge : CompositeDrawable, ISerialisableDrawable { - private SliderPath barPath = null!; - - private const float main_path_radius = 1f; - public bool UsesFixedAnchor { get; set; } [BackgroundDependencyLoader] @@ -28,30 +19,7 @@ namespace osu.Game.Screens.Play.HUD { AutoSizeAxes = Axes.Both; - const float bar_length = 430 - main_path_radius * 2; - const float bar_top = 0; - const float bar_bottom = 80; - const float curve_start = bar_length - 105; - const float curve_end = bar_length - 35; - - const float curve_smoothness = 10; - - Vector2 diagonalDir = (new Vector2(curve_end, bar_top) - new Vector2(curve_start, bar_bottom)).Normalized(); - - barPath = new SliderPath(new[] - { - new PathControlPoint(new Vector2(0, bar_bottom), PathType.Linear), - new PathControlPoint(new Vector2(curve_start - curve_smoothness, bar_bottom), PathType.Bezier), - new PathControlPoint(new Vector2(curve_start, bar_bottom)), - new PathControlPoint(new Vector2(curve_start, bar_bottom) + diagonalDir * curve_smoothness, PathType.Linear), - new PathControlPoint(new Vector2(curve_end, bar_top) - diagonalDir * curve_smoothness, PathType.Bezier), - new PathControlPoint(new Vector2(curve_end, bar_top)), - new PathControlPoint(new Vector2(curve_end + curve_smoothness, bar_top), PathType.Linear), - new PathControlPoint(new Vector2(bar_length, bar_top)), - }); - var vertices = new List(); - barPath.GetPathToProgress(vertices, 0, 1); InternalChildren = new Drawable[] { @@ -66,17 +34,6 @@ namespace osu.Game.Screens.Play.HUD WedgeHeight = { Value = 72 }, Position = new Vector2(4, 5) }, - new SmoothPath - { - Colour = Color4.White, - PathRadius = 1f, - Vertices = vertices, - }, - new Circle - { - Y = bar_bottom - 1.5f + main_path_radius, - Size = new Vector2(300f, 3f), - } }; } } diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 6c4074fd92..42ab93d3c5 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -136,7 +136,7 @@ namespace osu.Game.Skinning if (scoreWedge != null) { - scoreWedge.Position = new Vector2(-50, 50); + scoreWedge.Position = new Vector2(-50, 15); if (score != null) score.Position = new Vector2(components_x_offset, scoreWedge.Y + 15); From 4c7db4c2625483d557f1e4b6964579adcb7a0060 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 7 Nov 2023 00:49:22 +0300 Subject: [PATCH 078/207] Make score counter right-aligned --- osu.Game/Skinning/ArgonSkin.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 42ab93d3c5..e00973f710 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -139,7 +139,10 @@ namespace osu.Game.Skinning scoreWedge.Position = new Vector2(-50, 15); if (score != null) - score.Position = new Vector2(components_x_offset, scoreWedge.Y + 15); + { + score.Origin = Anchor.TopRight; + score.Position = new Vector2(components_x_offset + 200, scoreWedge.Y + 30); + } if (accuracy != null) { From ce36884ef05dbe1664e93ef9285abb43b0849067 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 7 Nov 2023 00:54:34 +0300 Subject: [PATCH 079/207] Make score wireframes display up to required digits count --- .../Screens/Play/HUD/ArgonScoreCounter.cs | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index 03635f2914..5d40dd81a3 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -1,9 +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 System; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -19,7 +21,7 @@ namespace osu.Game.Screens.Play.HUD public partial class ArgonScoreCounter : GameplayScoreCounter, ISerialisableDrawable { [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] - public BindableFloat WireframeOpactiy { get; } = new BindableFloat(0.4f) + public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) { Precision = 0.01f, MinValue = 0, @@ -28,9 +30,12 @@ namespace osu.Game.Screens.Play.HUD public bool UsesFixedAnchor { get; set; } + protected override LocalisableString FormatCount(long count) => count.ToLocalisableString(); + protected override IHasText CreateText() => new ArgonScoreTextComponent { - WireframeOpactiy = { BindTarget = WireframeOpactiy }, + RequiredDisplayDigits = { BindTarget = RequiredDisplayDigits }, + WireframeOpacity = { BindTarget = WireframeOpacity }, }; private partial class ArgonScoreTextComponent : CompositeDrawable, IHasText @@ -38,14 +43,15 @@ namespace osu.Game.Screens.Play.HUD private readonly ArgonScoreSpriteText wireframesPart; private readonly ArgonScoreSpriteText textPart; - public IBindable WireframeOpactiy { get; } = new BindableFloat(); + public IBindable RequiredDisplayDigits { get; } = new BindableInt(); + public IBindable WireframeOpacity { get; } = new BindableFloat(); public LocalisableString Text { get => textPart.Text; set { - wireframesPart.Text = value; + wireframesPart.Text = new string('#', Math.Max(value.ToString().Length, RequiredDisplayDigits.Value)); textPart.Text = value; } } @@ -56,15 +62,23 @@ namespace osu.Game.Screens.Play.HUD InternalChildren = new[] { - wireframesPart = new ArgonScoreSpriteText(@"wireframes"), - textPart = new ArgonScoreSpriteText(), + wireframesPart = new ArgonScoreSpriteText(@"wireframes") + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + textPart = new ArgonScoreSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, }; } protected override void LoadComplete() { base.LoadComplete(); - WireframeOpactiy.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true); + WireframeOpacity.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true); } private partial class ArgonScoreSpriteText : OsuSpriteText From 7c1c62ba8aef898bddaa6c07124a1053251bdf8a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 7 Nov 2023 01:58:26 +0300 Subject: [PATCH 080/207] Remove argon combo wedge and update combo counter position --- osu.Game/Screens/Play/HUD/ArgonComboWedge.cs | 27 -------------------- osu.Game/Skinning/ArgonSkin.cs | 21 +++++---------- 2 files changed, 7 insertions(+), 41 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/ArgonComboWedge.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonComboWedge.cs b/osu.Game/Screens/Play/HUD/ArgonComboWedge.cs deleted file mode 100644 index 6da3727505..0000000000 --- a/osu.Game/Screens/Play/HUD/ArgonComboWedge.cs +++ /dev/null @@ -1,27 +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.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - public partial class ArgonComboWedge : CompositeDrawable, ISerialisableDrawable - { - public bool UsesFixedAnchor { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - AutoSizeAxes = Axes.Both; - - InternalChild = new ArgonWedgePiece - { - WedgeWidth = { Value = 186 }, - WedgeHeight = { Value = 33 }, - }; - } - } -} diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index e00973f710..82c150ced7 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -118,7 +118,6 @@ namespace osu.Game.Skinning var scoreWedge = container.OfType().FirstOrDefault(); var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); - var comboWedge = container.OfType().FirstOrDefault(); var combo = container.OfType().FirstOrDefault(); var songProgress = container.OfType().FirstOrDefault(); var keyCounter = container.OfType().FirstOrDefault(); @@ -153,18 +152,6 @@ namespace osu.Game.Skinning } } - if (comboWedge != null) - { - comboWedge.Position = new Vector2(-12, 130); - - if (combo != null) - { - combo.Anchor = Anchor.TopLeft; - combo.Origin = Anchor.TopLeft; - combo.Position = new Vector2(components_x_offset, comboWedge.Y - 2); - } - } - var hitError = container.OfType().FirstOrDefault(); if (hitError != null) @@ -199,6 +186,13 @@ namespace osu.Game.Skinning keyCounter.Origin = Anchor.BottomRight; keyCounter.Position = new Vector2(-(hitError.Width + padding), -(padding * 2 + song_progress_offset_height)); } + + if (combo != null && hitError != null) + { + combo.Anchor = Anchor.BottomLeft; + combo.Origin = Anchor.BottomLeft; + combo.Position = new Vector2(hitError.Width + padding, -50); + } } } }) @@ -209,7 +203,6 @@ namespace osu.Game.Skinning new ArgonScoreWedge(), new ArgonScoreCounter(), new ArgonAccuracyCounter(), - new ArgonComboWedge(), new ArgonComboCounter(), new BarHitErrorMeter(), new BarHitErrorMeter(), From 0dbba13686388559a25558ebbd4a4fd17c858e1d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 7 Nov 2023 01:59:00 +0300 Subject: [PATCH 081/207] Split argon score sprite text and update combo counter design --- .../Screens/Play/HUD/ArgonComboCounter.cs | 20 ++- .../Play/HUD/ArgonCounterTextComponent.cs | 152 ++++++++++++++++++ .../Screens/Play/HUD/ArgonScoreCounter.cs | 106 +----------- 3 files changed, 167 insertions(+), 111 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index 28c97c53aa..6a7d5ff665 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -2,28 +2,36 @@ // 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.Sprites; using osu.Framework.Localisation; -using osu.Game.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD { public partial class ArgonComboCounter : ComboCounter { + [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] + public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) + { + Precision = 0.01f, + MinValue = 0, + MaxValue = 1, + }; + [BackgroundDependencyLoader] private void load(ScoreProcessor scoreProcessor) { Current.BindTo(scoreProcessor.Combo); } - protected override OsuSpriteText CreateSpriteText() - => base.CreateSpriteText().With(s => s.Font = FontUsage.Default.With(size: 36f)); + protected override LocalisableString FormatCount(int count) => $@"{count}x"; - protected override LocalisableString FormatCount(int count) + protected override IHasText CreateText() => new ArgonCounterTextComponent(Anchor.TopLeft, "COMBO") { - return $@"{count}x"; - } + WireframeOpacity = { BindTarget = WireframeOpacity }, + }; } } diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs new file mode 100644 index 0000000000..9545168a46 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -0,0 +1,152 @@ +// 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.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Framework.Text; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonCounterTextComponent : CompositeDrawable, IHasText + { + private readonly LocalisableString? label; + + private readonly ArgonCounterSpriteText wireframesPart; + private readonly ArgonCounterSpriteText textPart; + + public IBindable RequiredDisplayDigits { get; } = new BindableInt(); + public IBindable WireframeOpacity { get; } = new BindableFloat(); + + public LocalisableString Text + { + get => textPart.Text; + set + { + wireframesPart.Text = new string('#', Math.Max(value.ToString().Count(char.IsDigit), RequiredDisplayDigits.Value)); + textPart.Text = value; + } + } + + public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null) + { + Anchor = anchor; + Origin = anchor; + + this.label = label; + + wireframesPart = new ArgonCounterSpriteText(@"wireframes") + { + Anchor = anchor, + Origin = anchor, + }; + textPart = new ArgonCounterSpriteText + { + Anchor = anchor, + Origin = anchor, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AutoSizeAxes = Axes.Both; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Alpha = label != null ? 1 : 0, + Text = label.GetValueOrDefault(), + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), + Colour = colours.Blue0, + Margin = new MarginPadding { Left = 2.5f }, + }, + new Container + { + AutoSizeAxes = Axes.Both, + Children = new[] + { + wireframesPart, + textPart, + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + WireframeOpacity.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true); + } + + private partial class ArgonCounterSpriteText : OsuSpriteText + { + private readonly string? glyphLookupOverride; + + private GlyphStore glyphStore = null!; + + protected override char FixedWidthReferenceCharacter => '5'; + + public ArgonCounterSpriteText(string? glyphLookupOverride = null) + { + this.glyphLookupOverride = glyphLookupOverride; + + Shadow = false; + UseFullGlyphHeight = false; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + // todo: rename font + Font = new FontUsage(@"argon-score", 1, fixedWidth: true); + Spacing = new Vector2(-2, 0); + + glyphStore = new GlyphStore(skin, glyphLookupOverride); + } + + protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); + + private class GlyphStore : ITexturedGlyphLookupStore + { + private readonly ISkin skin; + private readonly string? glyphLookupOverride; + + public GlyphStore(ISkin skin, string? glyphLookupOverride) + { + this.skin = skin; + this.glyphLookupOverride = glyphLookupOverride; + } + + public ITexturedCharacterGlyph? Get(string fontName, char character) + { + string lookup = glyphLookupOverride ?? character.ToString(); + var texture = skin.GetTexture($"{fontName}-{lookup}"); + + if (texture == null) + return null; + + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f); + } + + public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); + } + } + } +} diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index 5d40dd81a3..636565f181 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -1,20 +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 System.Threading.Tasks; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Framework.Text; using osu.Game.Configuration; -using osu.Game.Graphics.Sprites; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Screens.Play.HUD { @@ -32,107 +25,10 @@ namespace osu.Game.Screens.Play.HUD protected override LocalisableString FormatCount(long count) => count.ToLocalisableString(); - protected override IHasText CreateText() => new ArgonScoreTextComponent + protected override IHasText CreateText() => new ArgonCounterTextComponent(Anchor.TopRight) { RequiredDisplayDigits = { BindTarget = RequiredDisplayDigits }, WireframeOpacity = { BindTarget = WireframeOpacity }, }; - - private partial class ArgonScoreTextComponent : CompositeDrawable, IHasText - { - private readonly ArgonScoreSpriteText wireframesPart; - private readonly ArgonScoreSpriteText textPart; - - public IBindable RequiredDisplayDigits { get; } = new BindableInt(); - public IBindable WireframeOpacity { get; } = new BindableFloat(); - - public LocalisableString Text - { - get => textPart.Text; - set - { - wireframesPart.Text = new string('#', Math.Max(value.ToString().Length, RequiredDisplayDigits.Value)); - textPart.Text = value; - } - } - - public ArgonScoreTextComponent() - { - AutoSizeAxes = Axes.Both; - - InternalChildren = new[] - { - wireframesPart = new ArgonScoreSpriteText(@"wireframes") - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, - textPart = new ArgonScoreSpriteText - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - WireframeOpacity.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true); - } - - private partial class ArgonScoreSpriteText : OsuSpriteText - { - private readonly string? glyphLookupOverride; - - private GlyphStore glyphStore = null!; - - protected override char FixedWidthReferenceCharacter => '5'; - - public ArgonScoreSpriteText(string? glyphLookupOverride = null) - { - this.glyphLookupOverride = glyphLookupOverride; - - Shadow = false; - UseFullGlyphHeight = false; - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin) - { - Font = new FontUsage(@"argon-score", 1, fixedWidth: true); - Spacing = new Vector2(-2, 0); - - glyphStore = new GlyphStore(skin, glyphLookupOverride); - } - - protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); - - private class GlyphStore : ITexturedGlyphLookupStore - { - private readonly ISkin skin; - private readonly string? glyphLookupOverride; - - public GlyphStore(ISkin skin, string? glyphLookupOverride) - { - this.skin = skin; - this.glyphLookupOverride = glyphLookupOverride; - } - - public ITexturedCharacterGlyph? Get(string fontName, char character) - { - string lookup = glyphLookupOverride ?? character.ToString(); - var texture = skin.GetTexture($"{fontName}-{lookup}"); - - if (texture == null) - return null; - - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f); - } - - public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); - } - } - } } } From 05d941871860fc94db9d92d722d44eb4f190766a Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 7 Nov 2023 00:13:46 +0100 Subject: [PATCH 082/207] Rename setting to `TouchDisableGameplayTaps` for better visibility when searching --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 4 ++-- osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs | 2 +- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs | 2 +- osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- osu.Game/Screens/Play/PlayerSettings/InputSettings.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 19340aac15..25fe8170b1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -471,7 +471,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestInputWhileMouseButtonsDisabled() { - AddStep("Disable gameplay taps", () => config.SetValue(OsuSetting.GameplayDisableTaps, true)); + AddStep("Disable gameplay taps", () => config.SetValue(OsuSetting.TouchDisableGameplayTaps, true)); beginTouch(TouchSource.Touch1); @@ -623,7 +623,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("Release all touches", () => { config.SetValue(OsuSetting.MouseDisableButtons, false); - config.SetValue(OsuSetting.GameplayDisableTaps, false); + config.SetValue(OsuSetting.TouchDisableGameplayTaps, false); foreach (TouchSource source in InputManager.CurrentState.Touch.ActiveSources) InputManager.EndTouch(new Touch(source, osuInputManager.ScreenSpaceDrawQuad.Centre)); }); diff --git a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs index 994ec024b1..e815d7873e 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuTouchInputMapper.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.UI [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - tapsDisabled = config.GetBindable(OsuSetting.GameplayDisableTaps); + tapsDisabled = config.GetBindable(OsuSetting.TouchDisableGameplayTaps); } // Required to handle touches outside of the playfield when screen scaling is enabled. diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c44a089c49..6ef55ab919 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -108,7 +108,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.MouseDisableWheel, false); SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); - SetDefault(OsuSetting.GameplayDisableTaps, false); + SetDefault(OsuSetting.TouchDisableGameplayTaps, false); // Graphics SetDefault(OsuSetting.ShowFpsDisplay, false); @@ -415,6 +415,6 @@ namespace osu.Game.Configuration ReplaySettingsOverlay, AutomaticallyDownloadMissingBeatmaps, EditorShowSpeedChanges, - GameplayDisableTaps, + TouchDisableGameplayTaps, } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs index 793b707bfc..0056de6674 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Add(new SettingsCheckbox { LabelText = TouchSettingsStrings.DisableTapsDuringGameplay, - Current = osuConfig.GetBindable(OsuSetting.GameplayDisableTaps) + Current = osuConfig.GetBindable(OsuSetting.TouchDisableGameplayTaps) }); } diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index eb19368fc8..35d05b87c0 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.UI private void load(OsuConfigManager config) { mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); - tapsDisabled = config.GetBindable(OsuSetting.GameplayDisableTaps); + tapsDisabled = config.GetBindable(OsuSetting.TouchDisableGameplayTaps); } #region Action mapping (for replays) diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index 96b543d176..8a6e2759e3 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs @@ -27,6 +27,6 @@ namespace osu.Game.Screens.Play.PlayerSettings } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) => mouseButtonsCheckbox.Current = config.GetBindable(RuntimeInfo.IsDesktop ? OsuSetting.MouseDisableButtons : OsuSetting.GameplayDisableTaps); + private void load(OsuConfigManager config) => mouseButtonsCheckbox.Current = config.GetBindable(RuntimeInfo.IsDesktop ? OsuSetting.MouseDisableButtons : OsuSetting.DisableTapsDuringGameplay); } } From 7385c3c97bb48b507127dd8709d658788adbaf78 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 7 Nov 2023 00:17:15 +0100 Subject: [PATCH 083/207] Move `InputSettings` children creation code to BDL - Avoids now obsolete variable name - Makes changing to touch detection easier (access to session statics in BDL) --- .../Play/PlayerSettings/InputSettings.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index 8a6e2759e3..1387e01305 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs @@ -11,22 +11,23 @@ namespace osu.Game.Screens.Play.PlayerSettings { public partial class InputSettings : PlayerSettingsGroup { - private readonly PlayerCheckbox mouseButtonsCheckbox; - public InputSettings() : base("Input Settings") { - Children = new Drawable[] - { - mouseButtonsCheckbox = new PlayerCheckbox - { - // TODO: change to touchscreen detection once https://github.com/ppy/osu/pull/25348 makes it in - LabelText = RuntimeInfo.IsDesktop ? MouseSettingsStrings.DisableClicksDuringGameplay : TouchSettingsStrings.DisableTapsDuringGameplay - } - }; } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) => mouseButtonsCheckbox.Current = config.GetBindable(RuntimeInfo.IsDesktop ? OsuSetting.MouseDisableButtons : OsuSetting.DisableTapsDuringGameplay); + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new PlayerCheckbox + { + // TODO: change to touchscreen detection once https://github.com/ppy/osu/pull/25348 makes it in + LabelText = RuntimeInfo.IsDesktop ? MouseSettingsStrings.DisableClicksDuringGameplay : TouchSettingsStrings.DisableTapsDuringGameplay, + Current = config.GetBindable(RuntimeInfo.IsDesktop ? OsuSetting.MouseDisableButtons : OsuSetting.TouchDisableGameplayTaps) + } + }; + } } } From fcd73e62d2f98472e66524913c5f58fa2499d4f2 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 7 Nov 2023 13:06:14 +0100 Subject: [PATCH 084/207] Remove mobile specific changes Will be added back in a separate PR --- osu.Android/OsuGameAndroid.cs | 3 --- .../Overlays/Settings/Sections/Input/TouchSettings.cs | 11 ++++------- osu.Game/Screens/Play/PlayerSettings/InputSettings.cs | 5 ++--- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index e4b934a387..97a9848a12 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -98,9 +98,6 @@ namespace osu.Android case AndroidJoystickHandler jh: return new AndroidJoystickSettings(jh); - case AndroidTouchHandler: - return new TouchSettings(handler); - default: return base.CreateSettingsSubsectionFor(handler); } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs index 0056de6674..30a0b1b785 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs @@ -29,14 +29,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input [BackgroundDependencyLoader] private void load(OsuConfigManager osuConfig) { - if (!RuntimeInfo.IsMobile) // don't allow disabling the only input method (touch) on mobile. + Add(new SettingsCheckbox { - Add(new SettingsCheckbox - { - LabelText = CommonStrings.Enabled, - Current = handler.Enabled - }); - } + LabelText = CommonStrings.Enabled, + Current = handler.Enabled + }); Add(new SettingsCheckbox { diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index 1387e01305..47af4e0b53 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs @@ -23,9 +23,8 @@ namespace osu.Game.Screens.Play.PlayerSettings { new PlayerCheckbox { - // TODO: change to touchscreen detection once https://github.com/ppy/osu/pull/25348 makes it in - LabelText = RuntimeInfo.IsDesktop ? MouseSettingsStrings.DisableClicksDuringGameplay : TouchSettingsStrings.DisableTapsDuringGameplay, - Current = config.GetBindable(RuntimeInfo.IsDesktop ? OsuSetting.MouseDisableButtons : OsuSetting.TouchDisableGameplayTaps) + LabelText = MouseSettingsStrings.DisableClicksDuringGameplay, + Current = config.GetBindable(OsuSetting.MouseDisableButtons) } }; } From 00268d0ccce3691e682f7ae2ff08cb6a9a1f88f8 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 7 Nov 2023 13:09:30 +0100 Subject: [PATCH 085/207] Remove unused using --- osu.Android/OsuGameAndroid.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 97a9848a12..dea70e6b27 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -11,7 +11,6 @@ using osu.Framework.Input.Handlers; using osu.Framework.Platform; using osu.Game; using osu.Game.Overlays.Settings; -using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Updater; using osu.Game.Utils; From 02d9f005d09df3bd755a337a7d63c3ef4353d8b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Nov 2023 21:27:02 +0900 Subject: [PATCH 086/207] Update squirrel to latest release Quite a few fixes since our last update. See https://github.com/clowd/Clowd.Squirrel/releases. Of special note is [a fix for incomplete updates](https://github.com/clowd/Clowd.Squirrel/issues/182) which I believe we've seen cause issues in the wild before. --- 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 1d43e118a3..f37cfdc5f1 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -23,7 +23,7 @@ - + From 3e257f1e6c02795c6bbb6bf08f542707e8e6e827 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Nov 2023 23:21:51 +0900 Subject: [PATCH 087/207] Remove unused using statements --- osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs | 1 - osu.Game/Screens/Play/PlayerSettings/InputSettings.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs index 30a0b1b785..175fcc4709 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Input.Handlers; using osu.Framework.Localisation; diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index 47af4e0b53..852fbd8dcc 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.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 osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Configuration; From b092b0093affc97453d4f14be7b7a68005d5ae31 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 6 Nov 2023 21:13:36 +0300 Subject: [PATCH 088/207] Make sure bar draw quad is thick enough --- osu.Game/Graphics/UserInterface/BarGraph.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs index 27a41eb7e3..d3eebd71f0 100644 --- a/osu.Game/Graphics/UserInterface/BarGraph.cs +++ b/osu.Game/Graphics/UserInterface/BarGraph.cs @@ -145,6 +145,13 @@ namespace osu.Game.Graphics.UserInterface float barHeight = drawSize.Y * ((direction == BarDirection.TopToBottom || direction == BarDirection.BottomToTop) ? lengths[i] : barBreadth); float barWidth = drawSize.X * ((direction == BarDirection.LeftToRight || direction == BarDirection.RightToLeft) ? lengths[i] : barBreadth); + if (barHeight == 0 || barWidth == 0) + continue; + + // Make sure draw quad is thick enough + barHeight = Math.Max(barHeight, 1.5f); + barWidth = Math.Max(barWidth, 1.5f); + Vector2 topLeft; switch (direction) From cbea2db4bef9322b8dcbe9619d95cb918dcc3b55 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 7 Nov 2023 02:03:16 +0300 Subject: [PATCH 089/207] Support absolute-sized health bar and use it for default layout --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 30 +++++++++++++------ osu.Game/Skinning/ArgonSkin.cs | 3 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 793d43f7ef..2ae6bdcb15 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -36,12 +36,10 @@ namespace osu.Game.Screens.Play.HUD }; [SettingSource("Bar length")] - public BindableFloat BarLength { get; } = new BindableFloat(0.98f) - { - MinValue = 0.2f, - MaxValue = 1, - Precision = 0.01f, - }; + public BindableFloat BarLength { get; } = new BindableFloat(0.98f); + + [SettingSource("Use relative size")] + public BindableBool UseRelativeSize { get; } = new BindableBool(true); private BarPath mainBar = null!; @@ -140,9 +138,23 @@ namespace osu.Game.Screens.Play.HUD Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true); - BarLength.BindValueChanged(l => Width = l.NewValue, true); - BarHeight.BindValueChanged(_ => updatePath()); - updatePath(); + // update relative axes first before reading width from bar length. + RelativeSizeAxes = UseRelativeSize.Value ? Axes.X : Axes.None; + Width = BarLength.Value; + + UseRelativeSize.BindValueChanged(v => + { + RelativeSizeAxes = v.NewValue ? Axes.X : Axes.None; + float newWidth = Width; + + BarLength.MinValue = v.NewValue ? 0.2f : 200f; + BarLength.MaxValue = v.NewValue ? 1f : 1000f; + BarLength.Precision = v.NewValue ? 0.01f : 1f; + BarLength.Value = newWidth; + }, true); + + BarLength.ValueChanged += l => Width = l.NewValue; + BarHeight.BindValueChanged(_ => updatePath(), true); } protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 82c150ced7..95e1820059 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -129,7 +129,8 @@ namespace osu.Game.Skinning health.Anchor = Anchor.TopLeft; health.Origin = Anchor.TopLeft; - health.BarLength.Value = 0.22f; + health.UseRelativeSize.Value = false; + health.BarLength.Value = 300; health.BarHeight.Value = 30f; health.Position = new Vector2(components_x_offset, 20f); From e6d3085353886da1a196f6d0354eae86ba9469fa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Nov 2023 01:48:21 +0300 Subject: [PATCH 090/207] Update accuracy counter design --- .../Screens/Play/HUD/ArgonAccuracyCounter.cs | 20 +++++++++++++++---- .../Play/HUD/ArgonCounterTextComponent.cs | 8 ++++---- osu.Game/Skinning/ArgonSkin.cs | 14 ++++++------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 33223a526b..0414cbaea4 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -1,18 +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.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Screens.Play.HUD { public partial class ArgonAccuracyCounter : GameplayAccuracyCounter, ISerialisableDrawable { + [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] + public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) + { + Precision = 0.01f, + MinValue = 0, + MaxValue = 1, + }; + public bool UsesFixedAnchor { get; set; } - protected override OsuSpriteText CreateSpriteText() - => base.CreateSpriteText().With(s => s.Font = OsuFont.Default.With(size: 19.2f)); + protected override IHasText CreateText() => new ArgonCounterTextComponent(Anchor.TopLeft, "ACCURACY", new Vector2(-4, 0)) + { + WireframeOpacity = { BindTarget = WireframeOpacity }, + }; } } diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 9545168a46..fcad0f12d8 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.HUD } } - public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null) + public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null, Vector2? spacing = null) { Anchor = anchor; Origin = anchor; @@ -49,11 +49,13 @@ namespace osu.Game.Screens.Play.HUD { Anchor = anchor, Origin = anchor, + Spacing = spacing ?? new Vector2(-2, 0), }; textPart = new ArgonCounterSpriteText { Anchor = anchor, Origin = anchor, + Spacing = spacing ?? new Vector2(-2, 0), }; } @@ -115,9 +117,7 @@ namespace osu.Game.Screens.Play.HUD private void load(ISkinSource skin) { // todo: rename font - Font = new FontUsage(@"argon-score", 1, fixedWidth: true); - Spacing = new Vector2(-2, 0); - + Font = new FontUsage(@"argon-score", 1); glyphStore = new GlyphStore(skin, glyphLookupOverride); } diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 95e1820059..2af3a29804 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -143,14 +143,14 @@ namespace osu.Game.Skinning score.Origin = Anchor.TopRight; score.Position = new Vector2(components_x_offset + 200, scoreWedge.Y + 30); } + } - if (accuracy != null) - { - // +4 to vertically align the accuracy counter with the score counter. - accuracy.Position = new Vector2(components_x_offset + 4, scoreWedge.Y + 45); - accuracy.Anchor = Anchor.TopLeft; - accuracy.Origin = Anchor.TopLeft; - } + if (accuracy != null) + { + // +4 to vertically align the accuracy counter with the score counter. + accuracy.Position = new Vector2(-20, 20); + accuracy.Anchor = Anchor.TopRight; + accuracy.Origin = Anchor.TopRight; } var hitError = container.OfType().FirstOrDefault(); From d30bac3f49af02f2473e9c1a71aacbdb8f42dd4e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Nov 2023 01:49:13 +0300 Subject: [PATCH 091/207] Move "required display digits" feature to reside in argon score counter --- .../Screens/Play/HUD/ArgonCounterTextComponent.cs | 7 +++---- osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index fcad0f12d8..437a627cbc 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -25,7 +23,6 @@ namespace osu.Game.Screens.Play.HUD private readonly ArgonCounterSpriteText wireframesPart; private readonly ArgonCounterSpriteText textPart; - public IBindable RequiredDisplayDigits { get; } = new BindableInt(); public IBindable WireframeOpacity { get; } = new BindableFloat(); public LocalisableString Text @@ -33,7 +30,7 @@ namespace osu.Game.Screens.Play.HUD get => textPart.Text; set { - wireframesPart.Text = new string('#', Math.Max(value.ToString().Count(char.IsDigit), RequiredDisplayDigits.Value)); + wireframesPart.Text = FormatWireframes(value); textPart.Text = value; } } @@ -91,6 +88,8 @@ namespace osu.Game.Screens.Play.HUD }; } + protected virtual LocalisableString FormatWireframes(LocalisableString text) => text; + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index 636565f181..fef4199d31 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.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.LocalisationExtensions; using osu.Framework.Graphics; @@ -25,10 +26,22 @@ namespace osu.Game.Screens.Play.HUD protected override LocalisableString FormatCount(long count) => count.ToLocalisableString(); - protected override IHasText CreateText() => new ArgonCounterTextComponent(Anchor.TopRight) + protected override IHasText CreateText() => new ArgonScoreTextComponent(Anchor.TopRight) { RequiredDisplayDigits = { BindTarget = RequiredDisplayDigits }, WireframeOpacity = { BindTarget = WireframeOpacity }, }; + + private partial class ArgonScoreTextComponent : ArgonCounterTextComponent + { + public IBindable RequiredDisplayDigits { get; } = new BindableInt(); + + public ArgonScoreTextComponent(Anchor anchor, LocalisableString? label = null) + : base(anchor, label) + { + } + + protected override LocalisableString FormatWireframes(LocalisableString text) => new string('#', Math.Max(text.ToString().Length, RequiredDisplayDigits.Value)); + } } } From fdc714a248d1a9283649d12a11a14e7763bfa1dc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Nov 2023 02:05:19 +0300 Subject: [PATCH 092/207] Support percentages and ignore dot characters in wireframes part --- .../Play/HUD/ArgonCounterTextComponent.cs | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 437a627cbc..3d8546e0e3 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.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.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -42,13 +43,28 @@ namespace osu.Game.Screens.Play.HUD this.label = label; - wireframesPart = new ArgonCounterSpriteText(@"wireframes") + wireframesPart = new ArgonCounterSpriteText(c => + { + if (c == '.') + return @"dot"; + + return @"wireframes"; + }) { Anchor = anchor, Origin = anchor, Spacing = spacing ?? new Vector2(-2, 0), }; - textPart = new ArgonCounterSpriteText + textPart = new ArgonCounterSpriteText(c => + { + if (c == '.') + return @"dot"; + + if (c == '%') + return @"percentage"; + + return c.ToString(); + }) { Anchor = anchor, Origin = anchor, @@ -98,15 +114,15 @@ namespace osu.Game.Screens.Play.HUD private partial class ArgonCounterSpriteText : OsuSpriteText { - private readonly string? glyphLookupOverride; + private readonly Func getLookup; private GlyphStore glyphStore = null!; protected override char FixedWidthReferenceCharacter => '5'; - public ArgonCounterSpriteText(string? glyphLookupOverride = null) + public ArgonCounterSpriteText(Func getLookup) { - this.glyphLookupOverride = glyphLookupOverride; + this.getLookup = getLookup; Shadow = false; UseFullGlyphHeight = false; @@ -117,7 +133,7 @@ namespace osu.Game.Screens.Play.HUD { // todo: rename font Font = new FontUsage(@"argon-score", 1); - glyphStore = new GlyphStore(skin, glyphLookupOverride); + glyphStore = new GlyphStore(skin, getLookup); } protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); @@ -125,17 +141,17 @@ namespace osu.Game.Screens.Play.HUD private class GlyphStore : ITexturedGlyphLookupStore { private readonly ISkin skin; - private readonly string? glyphLookupOverride; + private readonly Func getLookup; - public GlyphStore(ISkin skin, string? glyphLookupOverride) + public GlyphStore(ISkin skin, Func getLookup) { this.skin = skin; - this.glyphLookupOverride = glyphLookupOverride; + this.getLookup = getLookup; } public ITexturedCharacterGlyph? Get(string fontName, char character) { - string lookup = glyphLookupOverride ?? character.ToString(); + string lookup = getLookup(character); var texture = skin.GetTexture($"{fontName}-{lookup}"); if (texture == null) From 4de5454538b0dff1b8b4a020a7f12fcb294ccc06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Nov 2023 02:06:51 +0300 Subject: [PATCH 093/207] Bring back left-side line next to health display Makes the score counter not look weird when it reaches 8 digits --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 9 +++--- .../Screens/Play/HUD/ArgonHealthRightLine.cs | 30 +++++++++++++++++++ osu.Game/Skinning/ArgonSkin.cs | 4 +++ 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ArgonHealthRightLine.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 2ae6bdcb15..b2b3181d08 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -90,12 +90,11 @@ namespace osu.Game.Screens.Play.HUD } } - private const float main_path_radius = 10f; + public const float MAIN_PATH_RADIUS = 10f; [BackgroundDependencyLoader] private void load() { - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; InternalChild = new Container @@ -105,7 +104,7 @@ namespace osu.Game.Screens.Play.HUD { background = new BackgroundPath { - PathRadius = main_path_radius, + PathRadius = MAIN_PATH_RADIUS, }, glowBar = new BarPath { @@ -125,7 +124,7 @@ namespace osu.Game.Screens.Play.HUD Blending = BlendingParameters.Additive, BarColour = main_bar_colour, GlowColour = main_bar_glow_colour, - PathRadius = main_path_radius, + PathRadius = MAIN_PATH_RADIUS, GlowPortion = 0.6f, }, } @@ -248,7 +247,7 @@ namespace osu.Game.Screens.Play.HUD private void updatePath() { - float barLength = DrawWidth - main_path_radius * 2; + float barLength = DrawWidth - MAIN_PATH_RADIUS * 2; float curveStart = barLength - 70; float curveEnd = barLength - 40; diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthRightLine.cs b/osu.Game/Screens/Play/HUD/ArgonHealthRightLine.cs new file mode 100644 index 0000000000..25918f679c --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonHealthRightLine.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public partial class ArgonHealthRightLine : CompositeDrawable, ISerialisableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(50f, ArgonHealthDisplay.MAIN_PATH_RADIUS * 2); + InternalChild = new Circle + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 45f, + Height = 3f, + }; + } + } +} diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 2af3a29804..e9953b57a7 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -115,6 +115,7 @@ namespace osu.Game.Skinning var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container => { var health = container.OfType().FirstOrDefault(); + var healthLine = container.OfType().FirstOrDefault(); var scoreWedge = container.OfType().FirstOrDefault(); var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); @@ -134,6 +135,9 @@ namespace osu.Game.Skinning health.BarHeight.Value = 30f; health.Position = new Vector2(components_x_offset, 20f); + if (healthLine != null) + healthLine.Y = health.Y; + if (scoreWedge != null) { scoreWedge.Position = new Vector2(-50, 15); From 07b7e13633862e85522f0c1778c8dc06ee62724d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Nov 2023 02:07:24 +0300 Subject: [PATCH 094/207] Place health display in front of the score wedge --- osu.Game/Skinning/ArgonSkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index e9953b57a7..d598c04891 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -204,9 +204,10 @@ namespace osu.Game.Skinning { Children = new Drawable[] { - new ArgonHealthDisplay(), new ArgonScoreWedge(), new ArgonScoreCounter(), + new ArgonHealthDisplay(), + new ArgonHealthRightLine(), new ArgonAccuracyCounter(), new ArgonComboCounter(), new BarHitErrorMeter(), From d0fea381b18b5f19450f4d93951e0aed152a85c8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Nov 2023 02:13:16 +0300 Subject: [PATCH 095/207] Update skin deserialisation archives --- .../Archives/modified-argon-20231108.osk | Bin 0 -> 1494 bytes .../Archives/modified-argon-pro-20231105.osk | Bin 1899 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk delete mode 100644 osu.Game.Tests/Resources/Archives/modified-argon-pro-20231105.osk diff --git a/osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk b/osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk new file mode 100644 index 0000000000000000000000000000000000000000..d07c5171007777784216e3ee8d4bc631a602bfb9 GIT binary patch literal 1494 zcmWIWW@Zs#U|`^2Xcf!|uh^e_WIvFX&Il4=U?|Sc%+t%v%RJg|n0LrQ#O1f`(IcWB z8+rX$ChiU?2~qTQ`Ixx>ZD`s5{%hHhb97nVPl+aV1Qjyunwa+G%d_XdW2RNSmguco zzj(5k`_vD>CNlYplEjTyH)@5XT{4unf(89(awmw8vmIBP&~Qzl#P2f&~x=b%nS8oW?ovp zURH5_-s)#T0Rf-9&-jLJ3JN%N*8il>X&>K^jABXeXnw#J9p;v=`-Hf zi&-Q&AHCE#pfYtw79*;wbc&?>&M`4CREjY$01<<4VrHI4sEc!cZb5!tYF4{*mY&!tVq5qJ#(U44BM31-%tO!8+D@iZlx95 z;!KaJH{@S%yxA^7zUQ(M1oD5&e1swB0b=fasoewEjYwjZ}S{?2x8 z#oJS#uHWPPSTxDSGK`AeX9?qUOa!LwkY@Svh^=Z%I-Wb)B1hq+%XCJ$EVJ$ zXJ+R)^s8?Q`@FZ`YqsyU+ZcQMZIx{P^0%Jbxpq(bqF;3>XVb|kyq~4n>fg2{u8WTg z*pbcVGvm_w&aOti$Nzpxo&Uf7S(v20+?36GZ~CL=0GX*tRcgSLuL;C_Kpc>hSecfY znv;Uam_?diI{rSVJingtJ{`U(Xa!prqwD8x@5VVVA{5q7TP?;A;LXS+!hpL#0~!kf zjUWnEyrJttFCn0M7#JF#LUqB*3v{jM*%+a<8d#=c&(!ET7HO literal 0 HcmV?d00001 diff --git a/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231105.osk b/osu.Game.Tests/Resources/Archives/modified-argon-pro-20231105.osk deleted file mode 100644 index 16f89502eff0bc46df9922fec370ee1d804170f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1899 zcmWIWW@Zs#U|`^2U=>IWU(2a$c^JquWCDpWFcfEJ=ILeTWuEQdo5y4*&{A!Gw1sPl z%y(f;r;?D8l}@c1Vtd%dzP&s7{PN3ry-NL4!aF>tXnFaYU#gL+{apE5!+TG>#M-L$ zizgfVX?3b5E|D`);hl1)bI*K-YnzT1HhnuR{6^sY4(_?fzVs-6-5KKe+J>9etx|rg zjG)EDr7qr^?{6wlsW-@<8S$Uztj4_1v=@t#?uGbnK47z=bZzo(gV~C)ChIm%Uj0Jj zn9UA@Z~Hg?IM(GODp|EM?)BA&2jBf!92Fiq*LK&-{&(U5C>}LoaOf%ndaxUad7&Q7 z%uCDH%PP*#Gwm_tI^-bW`g=~&Osm~FN$KihlhimKF^X;GbUrcPAW~59{krBG`~Lr{ z4m}q*f0|FVdpD0wkzCCs&j;b6_m7obKXPNo>^1(Y&e~=9xnAK5KQn>7ZqMN`A+F_z zn)nvaP`qtBXW!3)nUV=U4-?*Q?rOaCw|vz-c~l36?qoWA5f}^S6c`x9fDZIc%*^u$ zb#czmEy&MH%_}JeyY%hM`}wyF1diYT9qwY|?9y<_sI$E<@3o1qbM|iAkkBVHvL9W& z@#2lc;#L2D&vDQU@RyFAtT5|C!>7I7^|j~Ht#{fN%(m=?xLsCSyCL!Zy#JO{IqVZfoDx4sx10T08KL%XUT@v|qaQxInN8Sup`q1r zv(f8mHmUJ5Z)W{9ZYyX`i>b9#4B4#m+RJ+OjJ=PQ(w=cHH58g9;b^v#aV$GrthTgy>^{vX^<~pp_~0Hd#~SvR7WADnJ(QoPE5=_ zcC{e#jaOIQ~!qITd~rQOiRVTX)KldmQ%dB_Rh`6=7me;zH9BN zd~kQYbJfu|_A}p}Q}6wIc-yU}yZ@F2F1@wq=k)(wds_O#^O=kL&KSiwvu#P5_V|hq z`_1_q1E zzyL``1_onbM&Ji#g5dnT^x)K-)Z`Ly>d&3%pMBVX=ji*tBFCO5gqdwKdXbn?FJZlA z&0W1zcJ)x<;83n@hoijq{eCkcHIvhB#`nozzfABKR(!Jfs=bwS#ZukV)rDL-7nm$B zOMKaNb Date: Wed, 8 Nov 2023 02:13:35 +0300 Subject: [PATCH 096/207] Remove unused local --- osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs b/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs index fe495b421f..e8ade9e5c6 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreWedge.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.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,8 +18,6 @@ namespace osu.Game.Screens.Play.HUD { AutoSizeAxes = Axes.Both; - var vertices = new List(); - InternalChildren = new Drawable[] { new ArgonWedgePiece From 6c6baab1156f64388e5fcea3ad712cc7e24bb463 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Nov 2023 16:41:30 +0900 Subject: [PATCH 097/207] Reword comment to explain why --- osu.Game/Graphics/UserInterface/BarGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs index d3eebd71f0..0ac987e85b 100644 --- a/osu.Game/Graphics/UserInterface/BarGraph.cs +++ b/osu.Game/Graphics/UserInterface/BarGraph.cs @@ -148,7 +148,7 @@ namespace osu.Game.Graphics.UserInterface if (barHeight == 0 || barWidth == 0) continue; - // Make sure draw quad is thick enough + // Apply minimum sizing to hide the fact that we don't have fractional anti-aliasing. barHeight = Math.Max(barHeight, 1.5f); barWidth = Math.Max(barWidth, 1.5f); From 38847c3ac5383c9fb69b7bf0b38aa8ea36b23b39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Nov 2023 17:23:01 +0900 Subject: [PATCH 098/207] Change test to move only on a toggle step --- osu.Game.Tests/Visual/Online/TestSceneGraph.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneGraph.cs index eee29e0aeb..f4bde159e5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneGraph.cs @@ -13,10 +13,10 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public partial class TestSceneGraph : OsuTestScene { - private readonly BarGraph graph; - public TestSceneGraph() { + BarGraph graph; + Child = graph = new BarGraph { RelativeSizeAxes = Axes.Both, @@ -34,13 +34,14 @@ namespace osu.Game.Tests.Visual.Online AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom); AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight); AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft); - } - protected override void LoadComplete() - { - base.LoadComplete(); - - graph.MoveToY(-10, 1000).Then().MoveToY(10, 1000).Loop(); + AddToggleStep("Toggle movement", enabled => + { + if (enabled) + graph.MoveToY(-10, 1000).Then().MoveToY(10, 1000).Loop(); + else + graph.ClearTransforms(); + }); } } } From fc1a0cf645b345c1b2b0727313a4f11b207a3268 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 19 Oct 2023 16:20:10 +0900 Subject: [PATCH 099/207] Update `ButtonSystem` to use new sample names --- osu.Game/Screens/Menu/ButtonSystem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index bf2eba43c0..d26709151c 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -127,14 +127,14 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader(true)] private void load(AudioManager audio, IdleTracker idleTracker, GameHost host) { - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-edit-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-default-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); From 17aa079cb1cd1ee7ae22942aa582507c37037c4d Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 8 Nov 2023 22:08:05 +0900 Subject: [PATCH 100/207] Use new tiered 'back' samples --- osu.Game/Screens/Menu/ButtonSystem.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index d26709151c..8ebe4de8de 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Menu private readonly List buttonsTopLevel = new List(); private readonly List buttonsPlay = new List(); - private Sample sampleBack; + private Sample sampleBackToLogo; private readonly LogoTrackingContainer logoTrackingContainer; @@ -104,7 +104,7 @@ namespace osu.Game.Screens.Menu buttonArea.AddRange(new Drawable[] { new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), - backButton = new MainMenuButton(ButtonSystemStrings.Back, @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, + backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) { VisibleState = ButtonSystemState.Play, @@ -155,7 +155,7 @@ namespace osu.Game.Screens.Menu if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); - sampleBack = audio.Samples.Get(@"Menu/button-back-select"); + sampleBackToLogo = audio.Samples.Get(@"Menu/back-to-logo"); } private void onMultiplayer() @@ -260,7 +260,9 @@ namespace osu.Game.Screens.Menu { case ButtonSystemState.TopLevel: State = ButtonSystemState.Initial; - sampleBack?.Play(); + + // Samples are explicitly played here in response to user interaction and not when transitioning due to idle. + sampleBackToLogo?.Play(); return true; case ButtonSystemState.Play: From f0a1df06aced3051a93684588a6f21a0bf0fafd0 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 8 Nov 2023 22:13:35 +0900 Subject: [PATCH 101/207] Add 'swoosh' samples to accentuate `MainMenu` animations --- osu.Game/Screens/Menu/ButtonSystem.cs | 4 ++++ osu.Game/Screens/Menu/MainMenu.cs | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8ebe4de8de..20af7e4d4a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -86,6 +86,7 @@ namespace osu.Game.Screens.Menu private readonly List buttonsPlay = new List(); private Sample sampleBackToLogo; + private Sample sampleLogoSwoosh; private readonly LogoTrackingContainer logoTrackingContainer; @@ -156,6 +157,7 @@ namespace osu.Game.Screens.Menu if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); sampleBackToLogo = audio.Samples.Get(@"Menu/back-to-logo"); + sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh"); } private void onMultiplayer() @@ -263,6 +265,8 @@ namespace osu.Game.Screens.Menu // Samples are explicitly played here in response to user interaction and not when transitioning due to idle. sampleBackToLogo?.Play(); + sampleLogoSwoosh?.Play(); + return true; case ButtonSystemState.Play: diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 22040b4f0b..36e336e960 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -7,6 +7,8 @@ using System; using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -89,8 +91,10 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; private Container logoTarget; + private Sample reappearSampleSwoosh; + [BackgroundDependencyLoader(true)] - private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) + private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics, AudioManager audio) { holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); @@ -162,6 +166,8 @@ namespace osu.Game.Screens.Menu Buttons.OnSettings = () => settings?.ToggleVisibility(); Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); + reappearSampleSwoosh = audio.Samples.Get(@"Menu/reappear-swoosh"); + preloadSongSelect(); } @@ -291,6 +297,8 @@ namespace osu.Game.Screens.Menu { base.OnResuming(e); + reappearSampleSwoosh?.Play(); + ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next()); // we may have consumed our preloaded instance, so let's make another. From f69c2ea39b46fff223c8372e6dd1d2a6b2da5bce Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 8 Nov 2023 22:18:33 +0900 Subject: [PATCH 102/207] Implement sample choking/muting for `ButtonSystem` samples --- osu.Game/Screens/Menu/ButtonSystem.cs | 10 ++++++++++ osu.Game/Screens/Menu/MainMenu.cs | 2 ++ osu.Game/Screens/Menu/MainMenuButton.cs | 6 +++++- osu.Game/Screens/Menu/OsuLogo.cs | 10 +++++++++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 20af7e4d4a..13464d4927 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -199,6 +199,7 @@ namespace osu.Game.Screens.Menu { if (State == ButtonSystemState.Initial) { + StopSamplePlayback(); logo?.TriggerClick(); return true; } @@ -264,12 +265,14 @@ namespace osu.Game.Screens.Menu State = ButtonSystemState.Initial; // Samples are explicitly played here in response to user interaction and not when transitioning due to idle. + StopSamplePlayback(); sampleBackToLogo?.Play(); sampleLogoSwoosh?.Play(); return true; case ButtonSystemState.Play: + StopSamplePlayback(); backButton.TriggerClick(); return true; @@ -278,6 +281,13 @@ namespace osu.Game.Screens.Menu } } + public void StopSamplePlayback() + { + buttonsPlay.ForEach(button => button.StopSamplePlayback()); + buttonsTopLevel.ForEach(button => button.StopSamplePlayback()); + logo?.StopSamplePlayback(); + } + private bool onOsuLogo() { switch (state) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 36e336e960..0f73707544 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -297,6 +297,8 @@ namespace osu.Game.Screens.Menu { base.OnResuming(e); + // Ensures any playing `ButtonSystem` samples are stopped when returning to MainMenu (as to not overlap with the 'back' sample) + Buttons.StopSamplePlayback(); reappearSampleSwoosh?.Play(); ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next()); diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index c3a96e36a1..63fc34b4fb 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -51,6 +51,7 @@ namespace osu.Game.Screens.Menu private readonly Action clickAction; private Sample sampleClick; private Sample sampleHover; + private SampleChannel sampleChannel; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); @@ -225,7 +226,8 @@ namespace osu.Game.Screens.Menu private void trigger() { - sampleClick?.Play(); + sampleChannel = sampleClick?.GetChannel(); + sampleChannel?.Play(); clickAction?.Invoke(); @@ -237,6 +239,8 @@ namespace osu.Game.Screens.Menu public override bool HandleNonPositionalInput => state == ButtonState.Expanded; public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; + public void StopSamplePlayback() => sampleChannel?.Stop(); + protected override void Update() { iconText.Alpha = Math.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1); diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 8867ecfb2a..75ef8be02e 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -52,6 +52,8 @@ namespace osu.Game.Screens.Menu private readonly IntroSequence intro; private Sample sampleClick; + private SampleChannel sampleClickChannel; + private Sample sampleBeat; private Sample sampleDownbeat; @@ -391,7 +393,11 @@ namespace osu.Game.Screens.Menu flashLayer.FadeOut(1500, Easing.OutExpo); if (Action?.Invoke() == true) - sampleClick.Play(); + { + StopSamplePlayback(); + sampleClickChannel = sampleClick.GetChannel(); + sampleClickChannel.Play(); + } return true; } @@ -440,6 +446,8 @@ namespace osu.Game.Screens.Menu private Container currentProxyTarget; private Drawable proxy; + public void StopSamplePlayback() => sampleClickChannel?.Stop(); + public Drawable ProxyToContainer(Container c) { if (currentProxyTarget != null) From 12a148d1084b1a6f11525ff16c223ecb6ddd6927 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Nov 2023 12:26:41 +0900 Subject: [PATCH 103/207] Use new initialisation structure for github sourced update manager --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 941ab335e8..dba157a6e9 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -10,8 +10,8 @@ using osu.Game; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Screens.Play; -using Squirrel; using Squirrel.SimpleSplat; +using Squirrel.Sources; using LogLevel = Squirrel.SimpleSplat.LogLevel; using UpdateManager = osu.Game.Updater.UpdateManager; @@ -63,7 +63,7 @@ namespace osu.Desktop.Updater if (localUserInfo?.IsPlaying.Value == true) return false; - updateManager ??= new GithubUpdateManager(@"https://github.com/ppy/osu", false, github_token, @"osulazer"); + updateManager ??= new Squirrel.UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false), @"osulazer"); var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false); From 8a47f05b1615e126c9841b52dd63576a5b1b521a Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 9 Nov 2023 14:01:33 +0900 Subject: [PATCH 104/207] Always play 'swoosh' sample when transitioning back to logo --- osu.Game/Screens/Menu/ButtonSystem.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 13464d4927..a0cf9f5322 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -267,7 +267,6 @@ namespace osu.Game.Screens.Menu // Samples are explicitly played here in response to user interaction and not when transitioning due to idle. StopSamplePlayback(); sampleBackToLogo?.Play(); - sampleLogoSwoosh?.Play(); return true; @@ -362,6 +361,9 @@ namespace osu.Game.Screens.Menu logo?.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo); logo?.ScaleTo(1, 800, Easing.OutExpo); }, buttonArea.Alpha * 150); + + if (lastState == ButtonSystemState.TopLevel) + sampleLogoSwoosh?.Play(); break; case ButtonSystemState.TopLevel: From 5d5c803cf6fc97c044551a8bd7d3d11f0db8f41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 9 Nov 2023 17:48:43 +0900 Subject: [PATCH 105/207] Add failing test case for touch device application during gameplay with incompatible mods active --- .../Mods/TestSceneOsuModTouchDevice.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index e41cc8cfbc..abeb56209e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -9,6 +9,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Input; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -113,6 +114,25 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); } + [Test] + public void TestIncompatibleModActive() + { + // this is only a veneer of enabling autopilot as having it actually active from the start is annoying to make happen + // given the tests' structure. + AddStep("enable autopilot", () => Player.Score.ScoreInfo.Mods = new Mod[] { new OsuModAutopilot() }); + + AddUntilStep("wait until 0 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0).Within(500)); + AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); + AddUntilStep("wait until 0", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); + AddStep("touch playfield", () => + { + var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + InputManager.BeginTouch(touch); + InputManager.EndTouch(touch); + }); + AddAssert("touch device mod not activated", () => Player.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); + } + [Test] public void TestSecondObjectTouched() { From 63a0ea54109c93984501b66435febf676663cab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 9 Nov 2023 17:50:57 +0900 Subject: [PATCH 106/207] Fix `PlayerTouchInputDetector` applying touch device mod when other inactive mods present --- osu.Game/Screens/Play/PlayerTouchInputDetector.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerTouchInputDetector.cs b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs index a5055dfb00..69c3cd0ded 100644 --- a/osu.Game/Screens/Play/PlayerTouchInputDetector.cs +++ b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; +using osu.Game.Utils; namespace osu.Game.Screens.Play { @@ -45,10 +46,15 @@ namespace osu.Game.Screens.Play if (touchDeviceMod == null) return; + var candidateMods = player.Score.ScoreInfo.Mods.Append(touchDeviceMod).ToArray(); + + if (!ModUtils.CheckCompatibleSet(candidateMods, out _)) + return; + // `Player` (probably rightly so) assumes immutability of mods, // so this will not be shown immediately on the mod display in the top right. // if this is to change, the mod immutability should be revisited. - player.Score.ScoreInfo.Mods = player.Score.ScoreInfo.Mods.Append(touchDeviceMod).ToArray(); + player.Score.ScoreInfo.Mods = candidateMods; } } } From e3e752b912ba0b14f9d2d2a96bc1695e65eb94cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Nov 2023 17:56:58 +0900 Subject: [PATCH 107/207] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 60c71a736d..73cd239854 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From ccb9ff826a87dbafbbeb0a0a572a404bc88c7e88 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Thu, 9 Nov 2023 13:09:59 +0100 Subject: [PATCH 108/207] fixed tests --- .../Online/TestSceneUserClickableAvatar.cs | 130 +++++++++--------- .../OnlinePlay/Components/ParticipantsList.cs | 2 +- .../DrawableRoomParticipantsList.cs | 2 +- osu.Game/Users/Drawables/ClickableAvatar.cs | 115 +++++++++++----- osu.Game/Users/Drawables/UpdateableAvatar.cs | 9 +- 5 files changed, 152 insertions(+), 106 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index 678767f15e..9217104aa8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -1,11 +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.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; +using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; using osu.Game.Users.Drawables; @@ -28,8 +30,10 @@ namespace osu.Game.Tests.Visual.Online Children = new[] { generateUser(@"peppy", 2, CountryCode.AU, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", false, "99EB47"), - generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", false), - generateUser(@"joshika39", 17032217, CountryCode.RS, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", true), + generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", true), + generateUser(@"joshika39", 17032217, CountryCode.RS, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", false), + new UpdateableAvatar(), + new UpdateableAvatar() }, }; }); @@ -37,68 +41,68 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestClickableAvatarHover() { - // AddStep($"click {1}. {nameof(ClickableAvatar)}", () => - // { - // var targets = this.ChildrenOfType().ToList(); - // if (targets.Count < 1) - // return; - // - // InputManager.MoveMouseTo(targets[0]); - // }); - // AddWaitStep("wait for tooltip to show", 5); - // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - // AddWaitStep("wait for tooltip to hide", 3); - // - // AddStep($"click {2}. {nameof(ClickableAvatar)}", () => - // { - // var targets = this.ChildrenOfType().ToList(); - // if (targets.Count < 2) - // return; - // - // InputManager.MoveMouseTo(targets[1]); - // }); - // AddWaitStep("wait for tooltip to show", 5); - // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - // AddWaitStep("wait for tooltip to hide", 3); - // - // AddStep($"click {3}. {nameof(ClickableAvatar)}", () => - // { - // var targets = this.ChildrenOfType().ToList(); - // if (targets.Count < 3) - // return; - // - // InputManager.MoveMouseTo(targets[2]); - // }); - // AddWaitStep("wait for tooltip to show", 5); - // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - // AddWaitStep("wait for tooltip to hide", 3); - // - // AddStep($"click null user {4}. {nameof(ClickableAvatar)}", () => - // { - // var targets = this.ChildrenOfType().ToList(); - // if (targets.Count < 4) - // return; - // - // InputManager.MoveMouseTo(targets[3]); - // }); - // AddWaitStep("wait for tooltip to show", 5); - // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - // AddWaitStep("wait for tooltip to hide", 3); - // - // AddStep($"click null user {5}. {nameof(ClickableAvatar)}", () => - // { - // var targets = this.ChildrenOfType().ToList(); - // if (targets.Count < 5) - // return; - // - // InputManager.MoveMouseTo(targets[4]); - // }); - // AddWaitStep("wait for tooltip to show", 5); - // AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - // AddWaitStep("wait for tooltip to hide", 3); + AddStep($"click user {1} with UserGridPanel {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 1) + return; + + InputManager.MoveMouseTo(targets[0]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click user {2} with username only. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 2) + return; + + InputManager.MoveMouseTo(targets[1]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click user {3} with UserGridPanel {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 3) + return; + + InputManager.MoveMouseTo(targets[2]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click null user {4}. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 4) + return; + + InputManager.MoveMouseTo(targets[3]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); + + AddStep($"click null user {5}. {nameof(ClickableAvatar)}", () => + { + var targets = this.ChildrenOfType().ToList(); + if (targets.Count < 5) + return; + + InputManager.MoveMouseTo(targets[4]); + }); + AddWaitStep("wait for tooltip to show", 5); + AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddWaitStep("wait for tooltip to hide", 3); } - private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool isTooltipEnabled, string? color = null) + private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool onlyUsername, string? color = null) { return new ClickableAvatar(new APIUser { @@ -121,7 +125,7 @@ namespace osu.Game.Tests.Visual.Online { Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), }, - IsTooltipEnabled = isTooltipEnabled, + ShowUsernameOnly = onlyUsername, }; } } diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs index cb1a846d6c..8cde7859b2 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.OnlinePlay.Components RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex(@"27252d"), }, - avatar = new UpdateableAvatar { RelativeSizeAxes = Axes.Both }, + avatar = new UpdateableAvatar(showUsernameOnly: true) { RelativeSizeAxes = Axes.Both }, }; } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 1814f5359f..65f0555612 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -289,7 +289,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components set => avatar.User = value; } - private readonly UpdateableAvatar avatar = new UpdateableAvatar { RelativeSizeAxes = Axes.Both }; + private readonly UpdateableAvatar avatar = new UpdateableAvatar(showUsernameOnly: true) { RelativeSizeAxes = Axes.Both }; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 376ce0b821..e7934016bc 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -1,44 +1,43 @@ // 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.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; -using osu.Game.Localisation; +using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osuTK; +using osuTK.Graphics; namespace osu.Game.Users.Drawables { - public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip + public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip { - public ITooltip GetCustomTooltip() - { - return new APIUserTooltip(user); - } + // public ITooltip GetCustomTooltip() => new APIUserTooltip(user!) { ShowTooltip = TooltipEnabled }; + public ITooltip GetCustomTooltip() => new APIUserTooltip(new APIUserTooltipContent(user!)); - public APIUser? TooltipContent => user; - - public override LocalisableString TooltipText - { - get - { - if (!Enabled.Value) - return string.Empty; - - return !IsTooltipEnabled ? (user?.Username ?? string.Empty) : ContextMenuStrings.ViewProfile; - } - set => throw new NotSupportedException(); - } - - public bool IsTooltipEnabled { get; set; } + public APIUserTooltipContent TooltipContent => content; + private readonly APIUserTooltipContent content; private readonly APIUser? user; + private bool tooltipEnabled; + + public override LocalisableString TooltipText => user!.Username; + + public bool ShowUsernameOnly + { + get => tooltipEnabled; + set + { + tooltipEnabled = value; + content.ShowUsernameOnly = ShowUsernameOnly; + } + } [Resolved] private OsuGame? game { get; set; } @@ -53,11 +52,8 @@ namespace osu.Game.Users.Drawables if (user?.Id != APIUser.SYSTEM_USER_ID) Action = openProfile; - } - public void SetValue(out bool value) - { - value = IsTooltipEnabled; + content = new APIUserTooltipContent(user!, ShowUsernameOnly); } [BackgroundDependencyLoader] @@ -80,23 +76,57 @@ namespace osu.Game.Users.Drawables return base.OnClick(e); } - public partial class APIUserTooltip : VisibilityContainer, ITooltip + public partial class APIUserTooltip : VisibilityContainer, ITooltip { - private APIUser? user; - - public APIUserTooltip(APIUser? user) + private OsuSpriteText text; + private APIUserTooltipContent content; + public APIUserTooltip(APIUserTooltipContent content) { - this.user = user; + this.content = content; + AutoSizeAxes = Axes.Both; + Masking = true; + CornerRadius = 5; + + Child = new UserGridPanel(content.User) + { + Width = 300 + }; + text = new OsuSpriteText() + { + Text = this.content.User.Username + }; } protected override void PopIn() { - if (user is null) + if (content.ShowUsernameOnly) { - return; + Child = new UserGridPanel(content.User) + { + Width = 300 + }; + } + else + { + Alpha = 0; + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray, + }, + text = new OsuSpriteText() + { + Font = FrameworkFont.Regular.With(size: 16), + Padding = new MarginPadding(5), + Text = content.User.Username + } + }; } - Child = new UserGridPanel(user); this.FadeIn(20, Easing.OutQuint); } @@ -104,9 +134,22 @@ namespace osu.Game.Users.Drawables public void Move(Vector2 pos) => Position = pos; - public void SetContent(APIUser user) + public void SetContent(APIUserTooltipContent content) { - this.user = user; + this.content = content; + text.Text = this.content.User.Username; + } + } + + public class APIUserTooltipContent + { + public APIUser User { get; } + public bool ShowUsernameOnly { get; set; } + + public APIUserTooltipContent(APIUser user, bool showUsernameOnly = false) + { + User = user; + ShowUsernameOnly = showUsernameOnly; } } } diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index 64d64c56ce..f6363f61e6 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -47,20 +47,20 @@ namespace osu.Game.Users.Drawables private readonly bool isInteractive; private readonly bool showGuestOnNull; - private readonly bool showUserPanel; + private readonly bool showUsernameOnly; /// /// Construct a new UpdateableAvatar. /// /// The initial user to display. /// If set to true, hover/click sounds will play and clicking the avatar will open the user's profile. - /// If set to true, the user status panel will be displayed in the tooltip. + /// If set to true, the user status panel will be displayed in the tooltip. /// Whether to show a default guest representation on null user (as opposed to nothing). - public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUserPanel = true, bool showGuestOnNull = true) + public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUsernameOnly = false, bool showGuestOnNull = true) { this.isInteractive = isInteractive; this.showGuestOnNull = showGuestOnNull; - this.showUserPanel = showUserPanel; + this.showUsernameOnly = showUsernameOnly; User = user; } @@ -75,7 +75,6 @@ namespace osu.Game.Users.Drawables return new ClickableAvatar(user) { RelativeSizeAxes = Axes.Both, - IsTooltipEnabled = showUserPanel }; } else From 4900a91c60fda24f76aeba4d2d2bf42cc1929e2d Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Thu, 9 Nov 2023 13:27:09 +0100 Subject: [PATCH 109/207] fixed static analysis problems and finished the implementation --- .../Online/TestSceneUserClickableAvatar.cs | 40 +++++++++---------- osu.Game/Users/Drawables/ClickableAvatar.cs | 12 +++--- osu.Game/Users/Drawables/UpdateableAvatar.cs | 11 +++-- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index 9217104aa8..50e5653ad5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -105,28 +105,28 @@ namespace osu.Game.Tests.Visual.Online private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool onlyUsername, string? color = null) { return new ClickableAvatar(new APIUser + { + Username = username, + Id = id, + CountryCode = countryCode, + CoverUrl = cover, + Colour = color ?? "000000", + Status = { - Username = username, - Id = id, - CountryCode = countryCode, - CoverUrl = cover, - Colour = color ?? "000000", - Status = - { - Value = new UserStatusOnline() - }, - }) + Value = new UserStatusOnline() + }, + }) + { + Width = 50, + Height = 50, + CornerRadius = 10, + Masking = true, + EdgeEffect = new EdgeEffectParameters { - Width = 50, - Height = 50, - CornerRadius = 10, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), - }, - ShowUsernameOnly = onlyUsername, - }; + Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), + }, + ShowUsernameOnly = onlyUsername, + }; } } } diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index e7934016bc..de0bcad497 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -21,9 +21,8 @@ namespace osu.Game.Users.Drawables // public ITooltip GetCustomTooltip() => new APIUserTooltip(user!) { ShowTooltip = TooltipEnabled }; public ITooltip GetCustomTooltip() => new APIUserTooltip(new APIUserTooltipContent(user!)); - public APIUserTooltipContent TooltipContent => content; + public APIUserTooltipContent TooltipContent { get; } - private readonly APIUserTooltipContent content; private readonly APIUser? user; private bool tooltipEnabled; @@ -35,7 +34,7 @@ namespace osu.Game.Users.Drawables set { tooltipEnabled = value; - content.ShowUsernameOnly = ShowUsernameOnly; + TooltipContent.ShowUsernameOnly = ShowUsernameOnly; } } @@ -53,7 +52,7 @@ namespace osu.Game.Users.Drawables if (user?.Id != APIUser.SYSTEM_USER_ID) Action = openProfile; - content = new APIUserTooltipContent(user!, ShowUsernameOnly); + TooltipContent = new APIUserTooltipContent(user!, ShowUsernameOnly); } [BackgroundDependencyLoader] @@ -80,6 +79,7 @@ namespace osu.Game.Users.Drawables { private OsuSpriteText text; private APIUserTooltipContent content; + public APIUserTooltip(APIUserTooltipContent content) { this.content = content; @@ -91,7 +91,7 @@ namespace osu.Game.Users.Drawables { Width = 300 }; - text = new OsuSpriteText() + text = new OsuSpriteText { Text = this.content.User.Username }; @@ -118,7 +118,7 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, Colour = Color4.Gray, }, - text = new OsuSpriteText() + text = new OsuSpriteText { Font = FrameworkFont.Regular.With(size: 16), Padding = new MarginPadding(5), diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index f6363f61e6..f220ee5a25 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -75,15 +75,14 @@ namespace osu.Game.Users.Drawables return new ClickableAvatar(user) { RelativeSizeAxes = Axes.Both, + ShowUsernameOnly = showUsernameOnly }; } - else + + return new DrawableAvatar(user) { - return new DrawableAvatar(user) - { - RelativeSizeAxes = Axes.Both, - }; - } + RelativeSizeAxes = Axes.Both, + }; } } } From 4fa158e0d832f44cddefac1b0dd7b94f879e3993 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Nov 2023 21:35:37 +0900 Subject: [PATCH 110/207] Split tournament player lists more equally --- .../Components/DrawableTeamWithPlayers.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/DrawableTeamWithPlayers.cs b/osu.Game.Tournament/Components/DrawableTeamWithPlayers.cs index 4f0c7d6b72..fd7a51140b 100644 --- a/osu.Game.Tournament/Components/DrawableTeamWithPlayers.cs +++ b/osu.Game.Tournament/Components/DrawableTeamWithPlayers.cs @@ -1,7 +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; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -17,6 +19,11 @@ namespace osu.Game.Tournament.Components { AutoSizeAxes = Axes.Both; + var players = team?.Players ?? new BindableList(); + + // split the players into two even columns, favouring the first column if odd. + int split = (int)Math.Ceiling(players.Count / 2f); + InternalChildren = new Drawable[] { new FillFlowContainer @@ -39,13 +46,13 @@ namespace osu.Game.Tournament.Components { Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, - ChildrenEnumerable = team?.Players.Select(createPlayerText).Take(5) ?? Enumerable.Empty() + ChildrenEnumerable = players.Take(split).Select(createPlayerText), }, new FillFlowContainer { Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, - ChildrenEnumerable = team?.Players.Select(createPlayerText).Skip(5) ?? Enumerable.Empty() + ChildrenEnumerable = players.Skip(split).Select(createPlayerText), }, } }, From 1ae3265f925911c3a1f7f640805765b68dc5d335 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Nov 2023 21:24:30 +0900 Subject: [PATCH 111/207] Update tests in line with new behaviour --- .../OsuDifficultyCalculatorTest.cs | 20 +++++------ .../TestSceneSliderInput.cs | 36 ++++++++++++------- .../Rulesets/Scoring/HitResultTest.cs | 2 +- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 7b7deb9c67..1d76c24620 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,22 +15,22 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.710442985146793d, 206, "diffcalc-test")] - [TestCase(1.4386882251130073d, 45, "zero-length-sliders")] - [TestCase(0.42506480230838789d, 2, "very-fast-slider")] - [TestCase(0.14102693012101306d, 1, "nan-slider")] + [TestCase(6.710442985146793d, 239, "diffcalc-test")] + [TestCase(1.4386882251130073d, 54, "zero-length-sliders")] + [TestCase(0.42506480230838789d, 4, "very-fast-slider")] + [TestCase(0.14102693012101306d, 2, "nan-slider")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(8.9742952703071666d, 206, "diffcalc-test")] - [TestCase(0.55071082800473514d, 2, "very-fast-slider")] - [TestCase(1.743180218215227d, 45, "zero-length-sliders")] + [TestCase(8.9742952703071666d, 239, "diffcalc-test")] + [TestCase(0.55071082800473514d, 4, "very-fast-slider")] + [TestCase(1.743180218215227d, 54, "zero-length-sliders")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime()); - [TestCase(6.710442985146793d, 239, "diffcalc-test")] - [TestCase(0.42506480230838789d, 4, "very-fast-slider")] - [TestCase(1.4386882251130073d, 54, "zero-length-sliders")] + [TestCase(6.710442985146793d, 272, "diffcalc-test")] + [TestCase(0.42506480230838789d, 6, "very-fast-slider")] + [TestCase(1.4386882251130073d, 63, "zero-length-sliders")] public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index f718a5069f..1e295aae70 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Tests if (hit) assertAllMaxJudgements(); else - AddAssert("Tracking dropped", assertMidSliderJudgementFail); + assertMidSliderJudgementFail(); AddAssert("Head judgement is first", () => judgementResults.First().HitObject is SliderHeadCircle); @@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 }, }); - AddAssert("Tracking lost", assertMidSliderJudgementFail); + assertMidSliderJudgementFail(); } /// @@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider }, }); - AddAssert("Tracking retained, sliderhead miss", assertHeadMissTailTracked); + assertHeadMissTailTracked(); } /// @@ -302,7 +302,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 }, }); - AddAssert("Tracking re-acquired", assertMidSliderJudgements); + assertMidSliderJudgements(); } /// @@ -328,7 +328,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 }, }); - AddAssert("Tracking lost", assertMidSliderJudgementFail); + assertMidSliderJudgementFail(); } /// @@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 }, }); - AddAssert("Tracking acquired", assertMidSliderJudgements); + assertMidSliderJudgements(); } /// @@ -373,7 +373,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 }, }); - AddAssert("Tracking acquired", assertMidSliderJudgements); + assertMidSliderJudgements(); } [Test] @@ -387,7 +387,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 }, }); - AddAssert("Tracking acquired", assertMidSliderJudgements); + assertMidSliderJudgements(); } /// @@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 }, }); - AddAssert("Tracking acquired", assertMidSliderJudgements); + assertMidSliderJudgements(); } /// @@ -454,7 +454,7 @@ namespace osu.Game.Rulesets.Osu.Tests new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end }, }); - AddAssert("Tracking dropped", assertMidSliderJudgementFail); + assertMidSliderJudgementFail(); } private void assertAllMaxJudgements() @@ -465,11 +465,21 @@ namespace osu.Game.Rulesets.Osu.Tests }, () => Is.EqualTo(judgementResults.Select(j => (j.HitObject, j.Judgement.MaxResult)))); } - private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.SmallTickHit && !judgementResults.First().IsHit; + private void assertHeadMissTailTracked() + { + AddAssert("Tracking retained", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit)); + AddAssert("Slider head misseed", () => judgementResults.First().IsHit, () => Is.False); + } - private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.SmallTickHit; + private void assertMidSliderJudgements() + { + AddAssert("Tracking acquired", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit)); + } - private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.SmallTickMiss; + private void assertMidSliderJudgementFail() + { + AddAssert("Tracking lost", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.IgnoreMiss)); + } private void performTest(List frames, Slider? slider = null, double? bpm = null, int? tickRate = null) { diff --git a/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs b/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs index 68d7335055..72acd18c5b 100644 --- a/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(new[] { HitResult.IgnoreHit }, new[] { HitResult.IgnoreMiss, HitResult.ComboBreak })] public void TestValidResultPairs(HitResult[] maxResults, HitResult[] minResults) { - HitResult[] unsupportedResults = HitResultExtensions.ALL_TYPES.Where(t => !minResults.Contains(t)).ToArray(); + HitResult[] unsupportedResults = HitResultExtensions.ALL_TYPES.Where(t => t != HitResult.IgnoreMiss && !minResults.Contains(t)).ToArray(); Assert.Multiple(() => { From edef31f426f8568afc3abf53b7611e525308c1d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Nov 2023 21:48:41 +0900 Subject: [PATCH 112/207] Correctly propagate classic behaviour flag to tail --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 7f2d9592af..cb6827b428 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -135,6 +135,8 @@ namespace osu.Game.Rulesets.Osu.Objects classicSliderBehaviour = value; if (HeadCircle != null) HeadCircle.ClassicSliderBehaviour = value; + if (TailCircle != null) + TailCircle.ClassicSliderBehaviour = value; } } @@ -218,6 +220,7 @@ namespace osu.Game.Rulesets.Osu.Objects StartTime = e.Time, Position = EndPosition, StackHeight = StackHeight, + ClassicSliderBehaviour = ClassicSliderBehaviour, }); break; From 615d8384abcc3c412a831a70a73ed0f6c6173467 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Nov 2023 22:27:29 +0900 Subject: [PATCH 113/207] Refactor everythign back to sanity --- .../Cards/Statistics/BeatmapCardStatistic.cs | 2 +- osu.Game/Users/Drawables/ClickableAvatar.cs | 93 +++---------------- 2 files changed, 13 insertions(+), 82 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/Statistics/BeatmapCardStatistic.cs b/osu.Game/Beatmaps/Drawables/Cards/Statistics/BeatmapCardStatistic.cs index 10de2b9128..6fd7142c05 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Statistics/BeatmapCardStatistic.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Statistics/BeatmapCardStatistic.cs @@ -74,7 +74,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics #region Tooltip implementation - public virtual ITooltip GetCustomTooltip() => null; + public virtual ITooltip GetCustomTooltip() => null!; public virtual object TooltipContent => null; #endregion diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index de0bcad497..48a12acb5e 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -5,38 +5,25 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; 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.Containers; -using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osuTK; -using osuTK.Graphics; namespace osu.Game.Users.Drawables { - public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip + public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip { // public ITooltip GetCustomTooltip() => new APIUserTooltip(user!) { ShowTooltip = TooltipEnabled }; - public ITooltip GetCustomTooltip() => new APIUserTooltip(new APIUserTooltipContent(user!)); + public ITooltip GetCustomTooltip() => new UserCardTooltip(); - public APIUserTooltipContent TooltipContent { get; } + public APIUser? TooltipContent { get; } private readonly APIUser? user; - private bool tooltipEnabled; - public override LocalisableString TooltipText => user!.Username; - - public bool ShowUsernameOnly - { - get => tooltipEnabled; - set - { - tooltipEnabled = value; - TooltipContent.ShowUsernameOnly = ShowUsernameOnly; - } - } + // TODO: reimplement. + public bool ShowUsernameOnly { get; set; } [Resolved] private OsuGame? game { get; set; } @@ -47,12 +34,10 @@ namespace osu.Game.Users.Drawables /// The user. A null value will get a placeholder avatar. public ClickableAvatar(APIUser? user = null) { - this.user = user; - if (user?.Id != APIUser.SYSTEM_USER_ID) Action = openProfile; - TooltipContent = new APIUserTooltipContent(user!, ShowUsernameOnly); + TooltipContent = this.user = user; } [BackgroundDependencyLoader] @@ -75,58 +60,17 @@ namespace osu.Game.Users.Drawables return base.OnClick(e); } - public partial class APIUserTooltip : VisibilityContainer, ITooltip + public partial class UserCardTooltip : VisibilityContainer, ITooltip { - private OsuSpriteText text; - private APIUserTooltipContent content; - - public APIUserTooltip(APIUserTooltipContent content) + public UserCardTooltip() { - this.content = content; AutoSizeAxes = Axes.Both; Masking = true; CornerRadius = 5; - - Child = new UserGridPanel(content.User) - { - Width = 300 - }; - text = new OsuSpriteText - { - Text = this.content.User.Username - }; } protected override void PopIn() { - if (content.ShowUsernameOnly) - { - Child = new UserGridPanel(content.User) - { - Width = 300 - }; - } - else - { - Alpha = 0; - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Gray, - }, - text = new OsuSpriteText - { - Font = FrameworkFont.Regular.With(size: 16), - Padding = new MarginPadding(5), - Text = content.User.Username - } - }; - } - this.FadeIn(20, Easing.OutQuint); } @@ -134,23 +78,10 @@ namespace osu.Game.Users.Drawables public void Move(Vector2 pos) => Position = pos; - public void SetContent(APIUserTooltipContent content) + public void SetContent(APIUser? content) => LoadComponentAsync(new UserGridPanel(content ?? new GuestUser()) { - this.content = content; - text.Text = this.content.User.Username; - } - } - - public class APIUserTooltipContent - { - public APIUser User { get; } - public bool ShowUsernameOnly { get; set; } - - public APIUserTooltipContent(APIUser user, bool showUsernameOnly = false) - { - User = user; - ShowUsernameOnly = showUsernameOnly; - } + Width = 300, + }, panel => Child = panel); } } } From 51cf85a9ab3806fe9294d33b24c52165a08aed6b Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 9 Nov 2023 13:22:49 +0100 Subject: [PATCH 114/207] Add touch input settings to android Also updates touch settings so the touch handler can't be disabled on mobile. --- osu.Android/OsuGameAndroid.cs | 4 ++++ .../Settings/Sections/Input/TouchSettings.cs | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index dea70e6b27..52cfb67f42 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Handlers; using osu.Framework.Platform; using osu.Game; using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Updater; using osu.Game.Utils; @@ -97,6 +98,9 @@ namespace osu.Android case AndroidJoystickHandler jh: return new AndroidJoystickSettings(jh); + case AndroidTouchHandler th: + return new TouchSettings(th); + default: return base.CreateSettingsSubsectionFor(handler); } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs index 175fcc4709..0056de6674 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Input.Handlers; using osu.Framework.Localisation; @@ -28,11 +29,14 @@ namespace osu.Game.Overlays.Settings.Sections.Input [BackgroundDependencyLoader] private void load(OsuConfigManager osuConfig) { - Add(new SettingsCheckbox + if (!RuntimeInfo.IsMobile) // don't allow disabling the only input method (touch) on mobile. { - LabelText = CommonStrings.Enabled, - Current = handler.Enabled - }); + Add(new SettingsCheckbox + { + LabelText = CommonStrings.Enabled, + Current = handler.Enabled + }); + } Add(new SettingsCheckbox { From 0c4c9aa4b515840611b3b39017ff73d04a3ecf1b Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 9 Nov 2023 14:37:10 +0100 Subject: [PATCH 115/207] Show touch taps setting in player loader on mobile platforms --- osu.Game/Screens/Play/PlayerSettings/InputSettings.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs b/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs index 852fbd8dcc..1387e01305 100644 --- a/osu.Game/Screens/Play/PlayerSettings/InputSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/InputSettings.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.Graphics; using osu.Game.Configuration; @@ -22,8 +23,9 @@ namespace osu.Game.Screens.Play.PlayerSettings { new PlayerCheckbox { - LabelText = MouseSettingsStrings.DisableClicksDuringGameplay, - Current = config.GetBindable(OsuSetting.MouseDisableButtons) + // TODO: change to touchscreen detection once https://github.com/ppy/osu/pull/25348 makes it in + LabelText = RuntimeInfo.IsDesktop ? MouseSettingsStrings.DisableClicksDuringGameplay : TouchSettingsStrings.DisableTapsDuringGameplay, + Current = config.GetBindable(RuntimeInfo.IsDesktop ? OsuSetting.MouseDisableButtons : OsuSetting.TouchDisableGameplayTaps) } }; } From 1b08f317fbd1fac55276dc05c29b0339667f2871 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 9 Nov 2023 15:12:24 +0100 Subject: [PATCH 116/207] Show touch input settings on iOS This does not cover android since `TouchHandler` is SDL-based. --- osu.Game/OsuGameBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1f46eb0c0d..f44dac5f5a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -575,14 +575,14 @@ namespace osu.Game case JoystickHandler jh: return new JoystickSettings(jh); - - case TouchHandler th: - return new TouchSettings(th); } } switch (handler) { + case TouchHandler th: + return new TouchSettings(th); + case MidiHandler: return new InputSection.HandlerSection(handler); From d4722a398892a18f14e868ff471e9132004da8fd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Nov 2023 17:20:05 +0300 Subject: [PATCH 117/207] Add failing test case --- .../TestSceneBackgroundScreenDefault.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index 1523ae7027..e902303505 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -181,6 +181,30 @@ namespace osu.Game.Tests.Visual.Background AddStep("restore default beatmap", () => Beatmap.SetDefault()); } + [Test] + public void TestBeatmapBackgroundWithStoryboardUnloadedOnSuspension() + { + BackgroundScreenBeatmap nestedScreen = null; + + setSupporter(true); + setSourceMode(BackgroundSource.BeatmapWithStoryboard); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithStoryboard()); + AddAssert("background changed", () => screen.CheckLastLoadChange() == true); + AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackgroundWithStoryboard)); + + AddUntilStep("storyboard present", () => screen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + + AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value))); + AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen()); + + AddUntilStep("storyboard unloaded", () => !screen.ChildrenOfType().Any()); + + AddStep("go back", () => screen.MakeCurrent()); + + AddUntilStep("storyboard reloaded", () => screen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + } + [Test] public void TestBackgroundTypeSwitch() { From bd8409219f079bcdd922ae2169d676608e512364 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 Nov 2023 06:37:29 +0300 Subject: [PATCH 118/207] Unload beatmap storyboard background when no longer present --- .../BeatmapBackgroundWithStoryboard.cs | 50 +++++++++++++++++-- osu.Game/Screens/BackgroundScreen.cs | 3 +- osu.Game/Screens/BackgroundScreenStack.cs | 3 ++ .../Backgrounds/BackgroundScreenDefault.cs | 22 ++++++++ 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 9c0d109ce4..e78a93396e 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -1,15 +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; using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Threading; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; +using osu.Game.Screens; using osu.Game.Storyboards.Drawables; namespace osu.Game.Graphics.Backgrounds @@ -18,6 +24,10 @@ namespace osu.Game.Graphics.Backgrounds { private readonly InterpolatingFramedClock storyboardClock; + private AudioContainer storyboardContainer = null!; + private DrawableStoryboard? drawableStoryboard; + private CancellationTokenSource? loadCancellationSource = new CancellationTokenSource(); + [Resolved(CanBeNull = true)] private MusicController? musicController { get; set; } @@ -33,18 +43,48 @@ namespace osu.Game.Graphics.Backgrounds [BackgroundDependencyLoader] private void load() { + AddInternal(storyboardContainer = new AudioContainer + { + RelativeSizeAxes = Axes.Both, + Volume = { Value = 0 }, + }); + + LoadStoryboard(); + } + + public void LoadStoryboard() + { + Debug.Assert(drawableStoryboard == null); + if (!Beatmap.Storyboard.HasDrawable) return; if (Beatmap.Storyboard.ReplacesBackground) Sprite.Alpha = 0; - LoadComponentAsync(new AudioContainer + LoadComponentAsync(drawableStoryboard = new DrawableStoryboard(Beatmap.Storyboard, mods.Value) { - RelativeSizeAxes = Axes.Both, - Volume = { Value = 0 }, - Child = new DrawableStoryboard(Beatmap.Storyboard, mods.Value) { Clock = storyboardClock } - }, AddInternal); + Clock = storyboardClock + }, s => + { + storyboardContainer.FadeInFromZero(BackgroundScreen.TRANSITION_LENGTH, Easing.OutQuint); + storyboardContainer.Add(s); + }, (loadCancellationSource = new CancellationTokenSource()).Token); + } + + public void UnloadStoryboard(Action scheduleStoryboardRemoval) + { + Debug.Assert(drawableStoryboard != null); + + loadCancellationSource.AsNonNull().Cancel(); + loadCancellationSource = null; + + DrawableStoryboard s = drawableStoryboard; + + storyboardContainer.FadeOut(BackgroundScreen.TRANSITION_LENGTH, Easing.OutQuint); + scheduleStoryboardRemoval(s); + + drawableStoryboard = null; } protected override void LoadComplete() diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index a7502f22d5..73af9b1bf2 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -13,7 +13,8 @@ namespace osu.Game.Screens { public abstract partial class BackgroundScreen : Screen, IEquatable { - protected const float TRANSITION_LENGTH = 500; + public const float TRANSITION_LENGTH = 500; + private const float x_movement_amount = 50; private readonly bool animateOnEnter; diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 99ca383b9f..3ec1835669 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Screens; +using osu.Game.Storyboards.Drawables; namespace osu.Game.Screens { @@ -33,5 +34,7 @@ namespace osu.Game.Screens base.Push(screen); return true; } + + public void ScheduleStoryboardDisposal(DrawableStoryboard storyboard) => Scheduler.AddDelayed(storyboard.RemoveAndDisposeImmediately, BackgroundScreen.TRANSITION_LENGTH); } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index d9554c10e2..66835363b4 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -3,11 +3,14 @@ #nullable disable +using System.Diagnostics; using System.Threading; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; +using osu.Framework.Screens; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -71,6 +74,25 @@ namespace osu.Game.Screens.Backgrounds void next() => Next(); } + public override void OnSuspending(ScreenTransitionEvent e) + { + var backgroundScreenStack = Parent as BackgroundScreenStack; + Debug.Assert(backgroundScreenStack != null); + + if (background is BeatmapBackgroundWithStoryboard storyboardBackground) + storyboardBackground.UnloadStoryboard(backgroundScreenStack.ScheduleStoryboardDisposal); + + base.OnSuspending(e); + } + + public override void OnResuming(ScreenTransitionEvent e) + { + if (background is BeatmapBackgroundWithStoryboard storyboardBackground) + storyboardBackground.LoadStoryboard(); + + base.OnResuming(e); + } + private ScheduledDelegate nextTask; private CancellationTokenSource cancellationTokenSource; From 768a31b2f5ec05907f2e1cf18d4bb3f63ae83da1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Nov 2023 22:56:48 +0300 Subject: [PATCH 119/207] Fix background crash on a beatmap with no storyboard --- .../Backgrounds/BeatmapBackgroundWithStoryboard.cs | 8 +++----- osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index e78a93396e..75cebb275f 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Threading; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Overlays; @@ -74,15 +73,14 @@ namespace osu.Game.Graphics.Backgrounds public void UnloadStoryboard(Action scheduleStoryboardRemoval) { - Debug.Assert(drawableStoryboard != null); + if (drawableStoryboard == null) + return; loadCancellationSource.AsNonNull().Cancel(); loadCancellationSource = null; - DrawableStoryboard s = drawableStoryboard; - storyboardContainer.FadeOut(BackgroundScreen.TRANSITION_LENGTH, Easing.OutQuint); - scheduleStoryboardRemoval(s); + scheduleStoryboardRemoval(drawableStoryboard); drawableStoryboard = null; } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 66835363b4..e22f61d806 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Threading; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; From cc9aeb53079f6f00af307fb0544e0335d9eebcc8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Nov 2023 22:57:12 +0300 Subject: [PATCH 120/207] Add test coverage --- .../TestSceneBackgroundScreenDefault.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index e902303505..37f2ee0b3f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -205,6 +205,30 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("storyboard reloaded", () => screen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); } + [Test] + public void TestBeatmapBackgroundWithStoryboardButBeatmapHasNone() + { + BackgroundScreenBeatmap nestedScreen = null; + + setSupporter(true); + setSourceMode(BackgroundSource.BeatmapWithStoryboard); + + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground()); + AddAssert("background changed", () => screen.CheckLastLoadChange() == true); + AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackgroundWithStoryboard)); + + AddUntilStep("no storyboard loaded", () => !screen.ChildrenOfType().Any()); + + AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value))); + AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen()); + + AddUntilStep("still no storyboard", () => !screen.ChildrenOfType().Any()); + + AddStep("go back", () => screen.MakeCurrent()); + + AddUntilStep("still no storyboard", () => !screen.ChildrenOfType().Any()); + } + [Test] public void TestBackgroundTypeSwitch() { From e9471589697083d8ea2214b299aca226085e7de9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Nov 2023 23:03:01 +0300 Subject: [PATCH 121/207] Remove fade out transition Unnecessary addition from this PR, makes the background fade to ugly black during transition between screens. --- osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 75cebb275f..9a3d64549b 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -79,7 +79,6 @@ namespace osu.Game.Graphics.Backgrounds loadCancellationSource.AsNonNull().Cancel(); loadCancellationSource = null; - storyboardContainer.FadeOut(BackgroundScreen.TRANSITION_LENGTH, Easing.OutQuint); scheduleStoryboardRemoval(drawableStoryboard); drawableStoryboard = null; From 59998b507acf084cc8d4ffbe85e16d1f3fab9229 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 9 Nov 2023 23:23:49 +0300 Subject: [PATCH 122/207] Hide background sprite when storyboard finishes loading --- .../BeatmapBackgroundWithStoryboard.cs | 16 +++++++++++----- osu.Game/Screens/BackgroundScreenStack.cs | 4 ++-- .../Backgrounds/BackgroundScreenDefault.cs | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 9a3d64549b..1e702967b6 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -58,20 +58,20 @@ namespace osu.Game.Graphics.Backgrounds if (!Beatmap.Storyboard.HasDrawable) return; - if (Beatmap.Storyboard.ReplacesBackground) - Sprite.Alpha = 0; - LoadComponentAsync(drawableStoryboard = new DrawableStoryboard(Beatmap.Storyboard, mods.Value) { Clock = storyboardClock }, s => { + if (Beatmap.Storyboard.ReplacesBackground) + Sprite.FadeOut(BackgroundScreen.TRANSITION_LENGTH, Easing.InQuint); + storyboardContainer.FadeInFromZero(BackgroundScreen.TRANSITION_LENGTH, Easing.OutQuint); storyboardContainer.Add(s); }, (loadCancellationSource = new CancellationTokenSource()).Token); } - public void UnloadStoryboard(Action scheduleStoryboardRemoval) + public void UnloadStoryboard(Action scheduleStoryboardRemoval) { if (drawableStoryboard == null) return; @@ -79,7 +79,13 @@ namespace osu.Game.Graphics.Backgrounds loadCancellationSource.AsNonNull().Cancel(); loadCancellationSource = null; - scheduleStoryboardRemoval(drawableStoryboard); + DrawableStoryboard s = drawableStoryboard; + + scheduleStoryboardRemoval(() => + { + s.RemoveAndDisposeImmediately(); + Sprite.Alpha = 1f; + }); drawableStoryboard = null; } diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 3ec1835669..9af6601aa4 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Screens; -using osu.Game.Storyboards.Drawables; namespace osu.Game.Screens { @@ -35,6 +35,6 @@ namespace osu.Game.Screens return true; } - public void ScheduleStoryboardDisposal(DrawableStoryboard storyboard) => Scheduler.AddDelayed(storyboard.RemoveAndDisposeImmediately, BackgroundScreen.TRANSITION_LENGTH); + internal void ScheduleToTransitionEnd(Action action) => Scheduler.AddDelayed(action, BackgroundScreen.TRANSITION_LENGTH); } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index e22f61d806..07b1cc6df4 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Backgrounds Debug.Assert(backgroundScreenStack != null); if (background is BeatmapBackgroundWithStoryboard storyboardBackground) - storyboardBackground.UnloadStoryboard(backgroundScreenStack.ScheduleStoryboardDisposal); + storyboardBackground.UnloadStoryboard(backgroundScreenStack.ScheduleToTransitionEnd); base.OnSuspending(e); } From 6f5d905ce766da9b2e3753585a41298118e9c6b9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 Nov 2023 00:34:29 +0300 Subject: [PATCH 123/207] Update argon accuracy counter design --- .../Screens/Play/HUD/ArgonAccuracyCounter.cs | 59 +++++++++++- .../Play/HUD/ArgonCounterTextComponent.cs | 94 ++++++++++--------- .../Screens/Play/HUD/ArgonScoreCounter.cs | 5 - 3 files changed, 107 insertions(+), 51 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 0414cbaea4..160841b40f 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -3,7 +3,9 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Skinning; using osuTK; @@ -22,9 +24,64 @@ namespace osu.Game.Screens.Play.HUD public bool UsesFixedAnchor { get; set; } - protected override IHasText CreateText() => new ArgonCounterTextComponent(Anchor.TopLeft, "ACCURACY", new Vector2(-4, 0)) + protected override IHasText CreateText() => new ArgonAccuracyTextComponent { WireframeOpacity = { BindTarget = WireframeOpacity }, }; + + private partial class ArgonAccuracyTextComponent : CompositeDrawable, IHasText + { + private readonly ArgonCounterTextComponent wholePart; + private readonly ArgonCounterTextComponent fractionPart; + + public IBindable WireframeOpacity { get; } = new BindableFloat(); + + public LocalisableString Text + { + get => wholePart.Text; + set + { + string[] split = value.ToString().Replace("%", string.Empty).Split("."); + + wholePart.Text = split[0]; + fractionPart.Text = "." + split[1]; + } + } + + public ArgonAccuracyTextComponent() + { + AutoSizeAxes = Axes.Both; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new Container + { + AutoSizeAxes = Axes.Both, + Child = wholePart = new ArgonCounterTextComponent(Anchor.TopRight, "ACCURACY") + { + RequiredDisplayDigits = { Value = 3 }, + WireframeOpacity = { BindTarget = WireframeOpacity } + } + }, + fractionPart = new ArgonCounterTextComponent(Anchor.TopLeft) + { + Margin = new MarginPadding { Top = 12f * 2f + 4f }, // +4 to account for the extra spaces above the digits. + WireframeOpacity = { BindTarget = WireframeOpacity }, + Scale = new Vector2(0.5f), + }, + new ArgonCounterTextComponent(Anchor.TopLeft) + { + Text = @"%", + Margin = new MarginPadding { Top = 12f }, + WireframeOpacity = { BindTarget = WireframeOpacity } + }, + } + }; + } + } } } diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 3d8546e0e3..759a5dbfea 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -19,62 +20,30 @@ namespace osu.Game.Screens.Play.HUD { public partial class ArgonCounterTextComponent : CompositeDrawable, IHasText { - private readonly LocalisableString? label; - private readonly ArgonCounterSpriteText wireframesPart; private readonly ArgonCounterSpriteText textPart; + private readonly OsuSpriteText labelText; public IBindable WireframeOpacity { get; } = new BindableFloat(); + public Bindable RequiredDisplayDigits { get; } = new BindableInt(); public LocalisableString Text { get => textPart.Text; set { - wireframesPart.Text = FormatWireframes(value); + int remainingCount = RequiredDisplayDigits.Value - value.ToString().Count(char.IsDigit); + string remainingText = remainingCount > 0 ? new string('#', remainingCount) : string.Empty; + + wireframesPart.Text = remainingText + value; textPart.Text = value; } } - public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null, Vector2? spacing = null) + public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null) { Anchor = anchor; Origin = anchor; - - this.label = label; - - wireframesPart = new ArgonCounterSpriteText(c => - { - if (c == '.') - return @"dot"; - - return @"wireframes"; - }) - { - Anchor = anchor, - Origin = anchor, - Spacing = spacing ?? new Vector2(-2, 0), - }; - textPart = new ArgonCounterSpriteText(c => - { - if (c == '.') - return @"dot"; - - if (c == '%') - return @"percentage"; - - return c.ToString(); - }) - { - Anchor = anchor, - Origin = anchor, - Spacing = spacing ?? new Vector2(-2, 0), - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { AutoSizeAxes = Axes.Both; InternalChild = new FillFlowContainer @@ -83,12 +52,11 @@ namespace osu.Game.Screens.Play.HUD Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuSpriteText + labelText = new OsuSpriteText { Alpha = label != null ? 1 : 0, Text = label.GetValueOrDefault(), Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), - Colour = colours.Blue0, Margin = new MarginPadding { Left = 2.5f }, }, new Container @@ -96,14 +64,50 @@ namespace osu.Game.Screens.Play.HUD AutoSizeAxes = Axes.Both, Children = new[] { - wireframesPart, - textPart, + wireframesPart = new ArgonCounterSpriteText(wireframesLookup) + { + Anchor = anchor, + Origin = anchor, + }, + textPart = new ArgonCounterSpriteText(textLookup) + { + Anchor = anchor, + Origin = anchor, + }, } } } }; } + private string textLookup(char c) + { + switch (c) + { + case '.': + return @"dot"; + + case '%': + return @"percentage"; + + default: + return c.ToString(); + } + } + + private string wireframesLookup(char c) + { + if (c == '.') return @"dot"; + + return @"wireframes"; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + labelText.Colour = colours.Blue0; + } + protected virtual LocalisableString FormatWireframes(LocalisableString text) => text; protected override void LoadComplete() @@ -131,8 +135,8 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load(ISkinSource skin) { - // todo: rename font - Font = new FontUsage(@"argon-score", 1); + Spacing = new Vector2(-2f, 0f); + Font = new FontUsage(@"argon-counter", 1); glyphStore = new GlyphStore(skin, getLookup); } diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index fef4199d31..b15c21801f 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.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.LocalisationExtensions; using osu.Framework.Graphics; @@ -34,14 +33,10 @@ namespace osu.Game.Screens.Play.HUD private partial class ArgonScoreTextComponent : ArgonCounterTextComponent { - public IBindable RequiredDisplayDigits { get; } = new BindableInt(); - public ArgonScoreTextComponent(Anchor anchor, LocalisableString? label = null) : base(anchor, label) { } - - protected override LocalisableString FormatWireframes(LocalisableString text) => new string('#', Math.Max(text.ToString().Length, RequiredDisplayDigits.Value)); } } } From 7fc2050f7227424b7e734260126146c48e99358e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 11:52:34 +0900 Subject: [PATCH 124/207] Rename variables and mak seconary tooltip type work again --- .../Online/TestSceneUserClickableAvatar.cs | 9 ++- .../OnlinePlay/Components/ParticipantsList.cs | 2 +- .../DrawableRoomParticipantsList.cs | 2 +- osu.Game/Users/Drawables/ClickableAvatar.cs | 74 ++++++++++++++----- osu.Game/Users/Drawables/UpdateableAvatar.cs | 11 ++- 5 files changed, 69 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index 50e5653ad5..93e2583eb7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -102,9 +102,9 @@ namespace osu.Game.Tests.Visual.Online AddWaitStep("wait for tooltip to hide", 3); } - private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool onlyUsername, string? color = null) + private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool showPanel, string? color = null) { - return new ClickableAvatar(new APIUser + var user = new APIUser { Username = username, Id = id, @@ -115,7 +115,9 @@ namespace osu.Game.Tests.Visual.Online { Value = new UserStatusOnline() }, - }) + }; + + return new ClickableAvatar(user, showPanel) { Width = 50, Height = 50, @@ -125,7 +127,6 @@ namespace osu.Game.Tests.Visual.Online { Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), }, - ShowUsernameOnly = onlyUsername, }; } } diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs index 8cde7859b2..c4aefe4f99 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.OnlinePlay.Components RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex(@"27252d"), }, - avatar = new UpdateableAvatar(showUsernameOnly: true) { RelativeSizeAxes = Axes.Both }, + avatar = new UpdateableAvatar(showUserPanelOnHover: true) { RelativeSizeAxes = Axes.Both }, }; } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 65f0555612..60e05285d9 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -289,7 +289,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components set => avatar.User = value; } - private readonly UpdateableAvatar avatar = new UpdateableAvatar(showUsernameOnly: true) { RelativeSizeAxes = Axes.Both }; + private readonly UpdateableAvatar avatar = new UpdateableAvatar(showUserPanelOnHover: true) { RelativeSizeAxes = Axes.Both }; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 48a12acb5e..6390acc608 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -1,12 +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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Cursor; +using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osuTK; @@ -15,15 +18,13 @@ namespace osu.Game.Users.Drawables { public partial class ClickableAvatar : OsuClickableContainer, IHasCustomTooltip { - // public ITooltip GetCustomTooltip() => new APIUserTooltip(user!) { ShowTooltip = TooltipEnabled }; - public ITooltip GetCustomTooltip() => new UserCardTooltip(); + public ITooltip GetCustomTooltip() => showCardOnHover ? new UserCardTooltip() : new NoCardTooltip(); public APIUser? TooltipContent { get; } private readonly APIUser? user; - // TODO: reimplement. - public bool ShowUsernameOnly { get; set; } + private readonly bool showCardOnHover; [Resolved] private OsuGame? game { get; set; } @@ -32,12 +33,15 @@ namespace osu.Game.Users.Drawables /// A clickable avatar for the specified user, with UI sounds included. /// /// The user. A null value will get a placeholder avatar. - public ClickableAvatar(APIUser? user = null) + /// + public ClickableAvatar(APIUser? user = null, bool showCardOnHover = false) { if (user?.Id != APIUser.SYSTEM_USER_ID) Action = openProfile; - TooltipContent = this.user = user; + this.showCardOnHover = showCardOnHover; + + TooltipContent = this.user = user ?? new GuestUser(); } [BackgroundDependencyLoader] @@ -65,23 +69,59 @@ namespace osu.Game.Users.Drawables public UserCardTooltip() { AutoSizeAxes = Axes.Both; - Masking = true; - CornerRadius = 5; } - protected override void PopIn() - { - this.FadeIn(20, Easing.OutQuint); - } - - protected override void PopOut() => this.FadeOut(80, Easing.OutQuint); + protected override void PopIn() => this.FadeIn(150, Easing.OutQuint); + protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint); public void Move(Vector2 pos) => Position = pos; - public void SetContent(APIUser? content) => LoadComponentAsync(new UserGridPanel(content ?? new GuestUser()) + private APIUser? user; + + public void SetContent(APIUser? content) { - Width = 300, - }, panel => Child = panel); + if (content == user && Children.Any()) + return; + + user = content; + + if (user != null) + { + LoadComponentAsync(new UserGridPanel(user) + { + Width = 300, + }, panel => Child = panel); + } + else + { + var tooltip = new OsuTooltipContainer.OsuTooltip(); + tooltip.SetContent(ContextMenuStrings.ViewProfile); + tooltip.Show(); + + Child = tooltip; + } + } + } + + public partial class NoCardTooltip : VisibilityContainer, ITooltip + { + public NoCardTooltip() + { + var tooltip = new OsuTooltipContainer.OsuTooltip(); + tooltip.SetContent(ContextMenuStrings.ViewProfile); + tooltip.Show(); + + Child = tooltip; + } + + protected override void PopIn() => this.FadeIn(150, Easing.OutQuint); + protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint); + + public void Move(Vector2 pos) => Position = pos; + + public void SetContent(APIUser? content) + { + } } } } diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index f220ee5a25..b020e7fa63 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -47,20 +47,20 @@ namespace osu.Game.Users.Drawables private readonly bool isInteractive; private readonly bool showGuestOnNull; - private readonly bool showUsernameOnly; + private readonly bool showUserPanelOnHover; /// /// Construct a new UpdateableAvatar. /// /// The initial user to display. /// If set to true, hover/click sounds will play and clicking the avatar will open the user's profile. - /// If set to true, the user status panel will be displayed in the tooltip. + /// If set to true, the user status panel will be displayed in the tooltip. /// Whether to show a default guest representation on null user (as opposed to nothing). - public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUsernameOnly = false, bool showGuestOnNull = true) + public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUserPanelOnHover = false, bool showGuestOnNull = true) { this.isInteractive = isInteractive; this.showGuestOnNull = showGuestOnNull; - this.showUsernameOnly = showUsernameOnly; + this.showUserPanelOnHover = showUserPanelOnHover; User = user; } @@ -72,10 +72,9 @@ namespace osu.Game.Users.Drawables if (isInteractive) { - return new ClickableAvatar(user) + return new ClickableAvatar(user, showUserPanelOnHover) { RelativeSizeAxes = Axes.Both, - ShowUsernameOnly = showUsernameOnly }; } From e0a5ec5352d0edf5d73513e203cdeda51624a302 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 12:09:16 +0900 Subject: [PATCH 125/207] Revert incorrect classic mod test assertions --- .../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 1d76c24620..fa7454b435 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Osu.Tests => base.Test(expectedStarRating, expectedMaxCombo, name); [TestCase(8.9742952703071666d, 239, "diffcalc-test")] - [TestCase(0.55071082800473514d, 4, "very-fast-slider")] [TestCase(1.743180218215227d, 54, "zero-length-sliders")] + [TestCase(0.55071082800473514d, 4, "very-fast-slider")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime()); - [TestCase(6.710442985146793d, 272, "diffcalc-test")] - [TestCase(0.42506480230838789d, 6, "very-fast-slider")] - [TestCase(1.4386882251130073d, 63, "zero-length-sliders")] + [TestCase(6.710442985146793d, 239, "diffcalc-test")] + [TestCase(1.4386882251130073d, 54, "zero-length-sliders")] + [TestCase(0.42506480230838789d, 4, "very-fast-slider")] public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); From fb02c317507b7c58770bdd52984eb6a0f91f9b53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 12:09:48 +0900 Subject: [PATCH 126/207] Fix typo in assert step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 1e295aae70..c9d721d1c4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -468,7 +468,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertHeadMissTailTracked() { AddAssert("Tracking retained", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit)); - AddAssert("Slider head misseed", () => judgementResults.First().IsHit, () => Is.False); + AddAssert("Slider head missed", () => judgementResults.First().IsHit, () => Is.False); } private void assertMidSliderJudgements() From f13648418b82f7158a733588e8caf4f094efc1ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 12:10:52 +0900 Subject: [PATCH 127/207] Update `HitResultTest` to be more conformant to original expectations --- osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs b/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs index 72acd18c5b..e003c9c534 100644 --- a/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/HitResultTest.cs @@ -11,14 +11,14 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestFixture] public class HitResultTest { - [TestCase(new[] { HitResult.Perfect, HitResult.Great, HitResult.Good, HitResult.Ok, HitResult.Meh }, new[] { HitResult.Miss })] - [TestCase(new[] { HitResult.LargeTickHit }, new[] { HitResult.LargeTickMiss })] - [TestCase(new[] { HitResult.SmallTickHit }, new[] { HitResult.SmallTickMiss })] + [TestCase(new[] { HitResult.Perfect, HitResult.Great, HitResult.Good, HitResult.Ok, HitResult.Meh }, new[] { HitResult.Miss, HitResult.IgnoreMiss })] + [TestCase(new[] { HitResult.LargeTickHit }, new[] { HitResult.LargeTickMiss, HitResult.IgnoreMiss })] + [TestCase(new[] { HitResult.SmallTickHit }, new[] { HitResult.SmallTickMiss, HitResult.IgnoreMiss })] [TestCase(new[] { HitResult.LargeBonus, HitResult.SmallBonus }, new[] { HitResult.IgnoreMiss })] [TestCase(new[] { HitResult.IgnoreHit }, new[] { HitResult.IgnoreMiss, HitResult.ComboBreak })] public void TestValidResultPairs(HitResult[] maxResults, HitResult[] minResults) { - HitResult[] unsupportedResults = HitResultExtensions.ALL_TYPES.Where(t => t != HitResult.IgnoreMiss && !minResults.Contains(t)).ToArray(); + HitResult[] unsupportedResults = HitResultExtensions.ALL_TYPES.Where(t => !minResults.Contains(t)).ToArray(); Assert.Multiple(() => { From 44c0442f4fc7f29f94ea1acad609ab8a676788d0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 10 Nov 2023 14:00:34 +0900 Subject: [PATCH 128/207] Adjust comment on Slider's judgement --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index cb6827b428..506145568e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -276,9 +276,9 @@ namespace osu.Game.Rulesets.Osu.Objects } public override Judgement CreateJudgement() => ClassicSliderBehaviour - // See logic in `DrawableSlider.CheckForResult()` + // Final combo is provided by the slider itself - see logic in `DrawableSlider.CheckForResult()` ? new OsuJudgement() - // Of note, this creates a combo discrepancy for non-classic-mod sliders (there is no combo increase for tail or slider judgement). + // Final combo is provided by the tail circle - see `SliderTailCircle` : new OsuIgnoreJudgement(); protected override HitWindows CreateHitWindows() => HitWindows.Empty; From b0c5b3cb1097861506b11862b996b91afe7a384a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 14:15:42 +0900 Subject: [PATCH 129/207] Add `Size` to serialised components of a `SerialisedDrawableInfo` --- osu.Game/Skinning/SerialisableDrawableExtensions.cs | 3 +++ osu.Game/Skinning/SerialisedDrawableInfo.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Skinning/SerialisableDrawableExtensions.cs b/osu.Game/Skinning/SerialisableDrawableExtensions.cs index 51b57a000d..0b44db9bdc 100644 --- a/osu.Game/Skinning/SerialisableDrawableExtensions.cs +++ b/osu.Game/Skinning/SerialisableDrawableExtensions.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Extensions; +using osuTK; namespace osu.Game.Skinning { @@ -18,6 +19,8 @@ namespace osu.Game.Skinning // todo: can probably make this better via deserialisation directly using a common interface. component.Position = drawableInfo.Position; component.Rotation = drawableInfo.Rotation; + if (drawableInfo.Size != Vector2.Zero && (component as CompositeDrawable)?.AutoSizeAxes == Axes.None) + component.Size = drawableInfo.Size; component.Scale = drawableInfo.Scale; component.Anchor = drawableInfo.Anchor; component.Origin = drawableInfo.Origin; diff --git a/osu.Game/Skinning/SerialisedDrawableInfo.cs b/osu.Game/Skinning/SerialisedDrawableInfo.cs index c515f228f7..0705e91d6d 100644 --- a/osu.Game/Skinning/SerialisedDrawableInfo.cs +++ b/osu.Game/Skinning/SerialisedDrawableInfo.cs @@ -35,6 +35,8 @@ namespace osu.Game.Skinning public Vector2 Scale { get; set; } + public Vector2 Size { get; set; } + public Anchor Anchor { get; set; } public Anchor Origin { get; set; } @@ -62,6 +64,7 @@ namespace osu.Game.Skinning Position = component.Position; Rotation = component.Rotation; Scale = component.Scale; + Size = component.Size; Anchor = component.Anchor; Origin = component.Origin; From ec3b6e47fb7c6b302e633132362bfe54873a58a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 14:17:11 +0900 Subject: [PATCH 130/207] Change selection handling to adjust `Size` instead of `Scale` for edge nodes --- .../Edit/OsuSelectionHandler.cs | 1 + .../SkinEditor/SkinSelectionHandler.cs | 40 +++++++++++++++++-- .../Edit/Compose/Components/SelectionBox.cs | 19 ++++++++- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index e81941d254..4da640a971 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Edit SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0; SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0; + SelectionBox.CanScaleProportionally = SelectionBox.CanScaleX && SelectionBox.CanScaleY; SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider); } diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index df73b15101..bf03906e56 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; @@ -31,8 +32,38 @@ namespace osu.Game.Overlays.SkinEditor UpdatePosition = updateDrawablePosition }; + private bool allSelectedSupportManualSizing => SelectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes == Axes.None); + public override bool HandleScale(Vector2 scale, Anchor anchor) { + bool adjustSize; + + switch (anchor) + { + // for corners, adjust scale. + case Anchor.TopLeft: + case Anchor.TopRight: + case Anchor.BottomLeft: + case Anchor.BottomRight: + adjustSize = false; + break; + + // for edges, adjust size. + case Anchor.TopCentre: + case Anchor.CentreLeft: + case Anchor.CentreRight: + case Anchor.BottomCentre: + // autosize elements can't be easily handled so just disable sizing for now. + if (!allSelectedSupportManualSizing) + return false; + + adjustSize = true; + break; + + default: + throw new ArgumentOutOfRangeException(nameof(anchor), anchor, null); + } + // convert scale to screen space scale = ToScreenSpace(scale) - ToScreenSpace(Vector2.Zero); @@ -120,7 +151,10 @@ namespace osu.Game.Overlays.SkinEditor if (Precision.AlmostEquals(MathF.Abs(drawableItem.Rotation) % 180, 90)) currentScaledDelta = new Vector2(scaledDelta.Y, scaledDelta.X); - drawableItem.Scale *= currentScaledDelta; + if (adjustSize) + drawableItem.Size *= currentScaledDelta; + else + drawableItem.Scale *= currentScaledDelta; } return true; @@ -169,8 +203,8 @@ namespace osu.Game.Overlays.SkinEditor { base.OnSelectionChanged(); - SelectionBox.CanScaleX = true; - SelectionBox.CanScaleY = true; + SelectionBox.CanScaleX = SelectionBox.CanScaleY = allSelectedSupportManualSizing; + SelectionBox.CanScaleProportionally = true; SelectionBox.CanFlipX = true; SelectionBox.CanFlipY = true; SelectionBox.CanReverse = false; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 72d96213ee..0917867a61 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -91,6 +91,23 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + private bool canScaleProportionally; + + /// + /// Whether vertical scaling support should be enabled. + /// + public bool CanScaleProportionally + { + get => canScaleProportionally; + set + { + if (canScaleProportionally == value) return; + + canScaleProportionally = value; + recreate(); + } + } + private bool canFlipX; /// @@ -245,7 +262,7 @@ namespace osu.Game.Screens.Edit.Compose.Components }; if (CanScaleX) addXScaleComponents(); - if (CanScaleX && CanScaleY) addFullScaleComponents(); + if (CanScaleProportionally) addFullScaleComponents(); if (CanScaleY) addYScaleComponents(); if (CanFlipX) addXFlipComponents(); if (CanFlipY) addYFlipComponents(); From fb361a4e0a8152bb35fe16cdc98bf06993f64a67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 14:25:55 +0900 Subject: [PATCH 131/207] Reset size along with scale for relative items --- osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index bf03906e56..dab8c1c558 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -249,7 +249,12 @@ namespace osu.Game.Overlays.SkinEditor yield return new OsuMenuItem("Reset scale", MenuItemType.Standard, () => { foreach (var blueprint in SelectedBlueprints) - ((Drawable)blueprint.Item).Scale = Vector2.One; + { + var blueprintItem = ((Drawable)blueprint.Item); + blueprintItem.Scale = Vector2.One; + if (RelativeSizeAxes == Axes.Both) + blueprintItem.Size = Vector2.One; + } }); yield return new EditorMenuItemSpacer(); From 0add035c674d59bd36ef129d474a76550e3ced92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 14:31:02 +0900 Subject: [PATCH 132/207] Disable resizing of `LegacySongProgress` Because it looks bad. --- osu.Game/Skinning/LegacySongProgress.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 22aea99291..26004ff111 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -19,11 +19,14 @@ namespace osu.Game.Skinning public override bool HandleNonPositionalInput => false; public override bool HandlePositionalInput => false; + public LegacySongProgress() + { + AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] private void load() { - Size = new Vector2(33); - InternalChildren = new Drawable[] { new Container @@ -39,7 +42,7 @@ namespace osu.Game.Skinning }, new CircularContainer { - RelativeSizeAxes = Axes.Both, + Size = new Vector2(33), Masking = true, BorderColour = Colour4.White, BorderThickness = 2, From 175dae49c623bfa8632608e204cac168d0b9723a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 14:43:51 +0900 Subject: [PATCH 133/207] Reset scale per axis --- osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index dab8c1c558..77e522a925 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -252,8 +252,11 @@ namespace osu.Game.Overlays.SkinEditor { var blueprintItem = ((Drawable)blueprint.Item); blueprintItem.Scale = Vector2.One; - if (RelativeSizeAxes == Axes.Both) - blueprintItem.Size = Vector2.One; + + if (blueprintItem.RelativeSizeAxes.HasFlagFast(Axes.X)) + blueprintItem.Width = 1; + if (blueprintItem.RelativeSizeAxes.HasFlagFast(Axes.Y)) + blueprintItem.Height = 1; } }); From 93ff82bc80c8bc06bc78149fd49584396ca982bd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 10 Nov 2023 14:52:03 +0900 Subject: [PATCH 134/207] Attempt to support quotes in handling of GH comment body --- .github/workflows/diffcalc.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index e7c628e365..d4150208d3 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -185,9 +185,11 @@ jobs: - name: Add comment environment if: ${{ github.event_name == 'issue_comment' }} + env: + COMMENT_BODY: ${{ github.event.comment.body }} run: | # Add comment environment - echo '${{ github.event.comment.body }}' | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do + echo $COMMENT_BODY | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do opt=$(echo ${line} | cut -d '=' -f1) sed -i "s;^${opt}=.*$;${line};" "${{ needs.directory.outputs.GENERATOR_ENV }}" done From f31c1c9c7941db573a9516710e42e53946947e25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 12:58:07 +0900 Subject: [PATCH 135/207] Rename and move skinnable line component to a more commomn place --- osu.Game/Skinning/ArgonSkin.cs | 5 +++-- .../Components/RoundedLine.cs} | 9 ++++----- 2 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/{Screens/Play/HUD/ArgonHealthRightLine.cs => Skinning/Components/RoundedLine.cs} (73%) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index d598c04891..b3eff41495 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -16,6 +16,7 @@ using osu.Game.IO; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.HitErrorMeters; +using osu.Game.Skinning.Components; using osuTK; using osuTK.Graphics; @@ -115,7 +116,7 @@ namespace osu.Game.Skinning var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container => { var health = container.OfType().FirstOrDefault(); - var healthLine = container.OfType().FirstOrDefault(); + var healthLine = container.OfType().FirstOrDefault(); var scoreWedge = container.OfType().FirstOrDefault(); var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); @@ -207,7 +208,7 @@ namespace osu.Game.Skinning new ArgonScoreWedge(), new ArgonScoreCounter(), new ArgonHealthDisplay(), - new ArgonHealthRightLine(), + new RoundedLine(), new ArgonAccuracyCounter(), new ArgonComboCounter(), new BarHitErrorMeter(), diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthRightLine.cs b/osu.Game/Skinning/Components/RoundedLine.cs similarity index 73% rename from osu.Game/Screens/Play/HUD/ArgonHealthRightLine.cs rename to osu.Game/Skinning/Components/RoundedLine.cs index 25918f679c..d7b12c3f4c 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthRightLine.cs +++ b/osu.Game/Skinning/Components/RoundedLine.cs @@ -5,19 +5,18 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Skinning; -using osuTK; -namespace osu.Game.Screens.Play.HUD +namespace osu.Game.Skinning.Components { - public partial class ArgonHealthRightLine : CompositeDrawable, ISerialisableDrawable + public partial class RoundedLine : CompositeDrawable, ISerialisableDrawable { public bool UsesFixedAnchor { get; set; } [BackgroundDependencyLoader] private void load() { - Size = new Vector2(50f, ArgonHealthDisplay.MAIN_PATH_RADIUS * 2); + AutoSizeAxes = Axes.Both; + InternalChild = new Circle { Anchor = Anchor.CentreLeft, From 99d9db5b76ba5292398201039e75fcb0a9bcc1d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 14:03:24 +0900 Subject: [PATCH 136/207] Use a better default size for line --- osu.Game/Skinning/ArgonSkin.cs | 7 ++++++- osu.Game/Skinning/Components/RoundedLine.cs | 19 ++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index b3eff41495..03b089582d 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -137,7 +137,12 @@ namespace osu.Game.Skinning health.Position = new Vector2(components_x_offset, 20f); if (healthLine != null) - healthLine.Y = health.Y; + { + healthLine.Anchor = Anchor.TopLeft; + healthLine.Origin = Anchor.CentreLeft; + healthLine.Y = health.Y + ArgonHealthDisplay.MAIN_PATH_RADIUS; + healthLine.Size = new Vector2(45, 3); + } if (scoreWedge != null) { diff --git a/osu.Game/Skinning/Components/RoundedLine.cs b/osu.Game/Skinning/Components/RoundedLine.cs index d7b12c3f4c..491f87d31e 100644 --- a/osu.Game/Skinning/Components/RoundedLine.cs +++ b/osu.Game/Skinning/Components/RoundedLine.cs @@ -1,29 +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.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osuTK; namespace osu.Game.Skinning.Components { - public partial class RoundedLine : CompositeDrawable, ISerialisableDrawable + public partial class RoundedLine : Circle, ISerialisableDrawable { public bool UsesFixedAnchor { get; set; } - [BackgroundDependencyLoader] - private void load() + public RoundedLine() { - AutoSizeAxes = Axes.Both; - - InternalChild = new Circle - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Width = 45f, - Height = 3f, - }; + Size = new Vector2(200, 8); } } } From 6c1d48dfaf0e4f06489b22c32b437a2d18d5250a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 14:51:14 +0900 Subject: [PATCH 137/207] Remove unused function --- osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 759a5dbfea..dbeafe5b59 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -108,8 +108,6 @@ namespace osu.Game.Screens.Play.HUD labelText.Colour = colours.Blue0; } - protected virtual LocalisableString FormatWireframes(LocalisableString text) => text; - protected override void LoadComplete() { base.LoadComplete(); From 7c3a626f4b2a1706cfc46295770b8673b374aee4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 15:06:07 +0900 Subject: [PATCH 138/207] Add basic animation for combo counter --- osu.Game/Screens/Play/HUD/ArgonComboCounter.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index 6a7d5ff665..36194d787b 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -8,11 +8,15 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Screens.Play.HUD { public partial class ArgonComboCounter : ComboCounter { + protected override double RollingDuration => 500; + protected override Easing RollingEasing => Easing.OutQuint; + [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) { @@ -25,6 +29,12 @@ namespace osu.Game.Screens.Play.HUD private void load(ScoreProcessor scoreProcessor) { Current.BindTo(scoreProcessor.Combo); + Current.BindValueChanged(combo => + { + bool wasIncrease = combo.NewValue > combo.OldValue; + DrawableCount.ScaleTo(new Vector2(1, wasIncrease ? 1.2f : 0.8f)) + .ScaleTo(Vector2.One, 600, Easing.OutQuint); + }); } protected override LocalisableString FormatCount(int count) => $@"{count}x"; From e861681cd4a081b3cc557df725cb0d2737096ebc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 15:06:19 +0900 Subject: [PATCH 139/207] Adjust argon rolling easings --- osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs | 3 +++ osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 160841b40f..ee5e16180e 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -14,6 +14,9 @@ namespace osu.Game.Screens.Play.HUD { public partial class ArgonAccuracyCounter : GameplayAccuracyCounter, ISerialisableDrawable { + protected override double RollingDuration => 500; + protected override Easing RollingEasing => Easing.OutQuint; + [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) { diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index b15c21801f..5ec359f5bb 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -13,6 +13,9 @@ namespace osu.Game.Screens.Play.HUD { public partial class ArgonScoreCounter : GameplayScoreCounter, ISerialisableDrawable { + protected override double RollingDuration => 500; + protected override Easing RollingEasing => Easing.OutQuint; + [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) { From 7e0b41219cd916931bd18d32f87705624b4e723b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 15:26:37 +0900 Subject: [PATCH 140/207] Change animation to only affect number portion, add miss animation --- .../Screens/Play/HUD/ArgonComboCounter.cs | 20 ++++++++++++++++--- .../Play/HUD/ArgonCounterTextComponent.cs | 4 +++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index 36194d787b..b75d4268fc 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.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.Graphics; @@ -9,11 +10,14 @@ using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { public partial class ArgonComboCounter : ComboCounter { + private ArgonCounterTextComponent text = null!; + protected override double RollingDuration => 500; protected override Easing RollingEasing => Easing.OutQuint; @@ -32,14 +36,24 @@ namespace osu.Game.Screens.Play.HUD Current.BindValueChanged(combo => { bool wasIncrease = combo.NewValue > combo.OldValue; - DrawableCount.ScaleTo(new Vector2(1, wasIncrease ? 1.2f : 0.8f)) - .ScaleTo(Vector2.One, 600, Easing.OutQuint); + bool wasMiss = combo.OldValue > 1 && combo.NewValue == 0; + + float newScale = Math.Clamp(text.NumberContainer.Scale.X * (wasIncrease ? 1.1f : 0.8f), 0.6f, 1.4f); + + float duration = wasMiss ? 2000 : 300; + + text.NumberContainer + .ScaleTo(new Vector2(newScale)) + .ScaleTo(Vector2.One, duration, Easing.OutQuint); + + if (wasMiss) + text.FlashColour(Color4.Red, duration, Easing.OutQuint); }); } protected override LocalisableString FormatCount(int count) => $@"{count}x"; - protected override IHasText CreateText() => new ArgonCounterTextComponent(Anchor.TopLeft, "COMBO") + protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopLeft, "COMBO") { WireframeOpacity = { BindTarget = WireframeOpacity }, }; diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index dbeafe5b59..56f60deae1 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -27,6 +27,8 @@ namespace osu.Game.Screens.Play.HUD public IBindable WireframeOpacity { get; } = new BindableFloat(); public Bindable RequiredDisplayDigits { get; } = new BindableInt(); + public Container NumberContainer { get; private set; } + public LocalisableString Text { get => textPart.Text; @@ -59,7 +61,7 @@ namespace osu.Game.Screens.Play.HUD Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), Margin = new MarginPadding { Left = 2.5f }, }, - new Container + NumberContainer = new Container { AutoSizeAxes = Axes.Both, Children = new[] From 4f90ac15fad7df54047abd01b6f88b6e28310277 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 15:39:17 +0900 Subject: [PATCH 141/207] Reduce the default wireframe opacity a bit --- osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs | 2 +- osu.Game/Screens/Play/HUD/ArgonComboCounter.cs | 2 +- osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index ee5e16180e..5f9441a5c4 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Play.HUD protected override Easing RollingEasing => Easing.OutQuint; [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] - public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) + public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f) { Precision = 0.01f, MinValue = 0, diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index b75d4268fc..63a17529f5 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play.HUD protected override Easing RollingEasing => Easing.OutQuint; [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] - public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) + public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f) { Precision = 0.01f, MinValue = 0, diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index 5ec359f5bb..0192fa3c02 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play.HUD protected override Easing RollingEasing => Easing.OutQuint; [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] - public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.4f) + public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f) { Precision = 0.01f, MinValue = 0, From 1818a84034c57f76335e06371b6a68296b0126f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 15:52:02 +0900 Subject: [PATCH 142/207] Rewrite tests to not rely on realtime clock Definitely faster, hopefully more reliable too. --- .../Mods/TestSceneOsuModTouchDevice.cs | 150 +++++++++++------- 1 file changed, 94 insertions(+), 56 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index abeb56209e..f528795125 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -5,6 +5,8 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Input; +using osu.Framework.Screens; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; @@ -14,39 +16,24 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osu.Game.Storyboards; using osu.Game.Tests.Visual; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Tests.Mods { - public partial class TestSceneOsuModTouchDevice : PlayerTestScene + public partial class TestSceneOsuModTouchDevice : RateAdjustedBeatmapTestScene { [Resolved] private SessionStatics statics { get; set; } = null!; - protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + private ScoreAccessibleSoloPlayer currentPlayer = null!; + private readonly ManualClock manualClock = new ManualClock { Rate = 0 }; - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => - new OsuBeatmap - { - HitObjects = - { - new HitCircle - { - Position = OsuPlayfield.BASE_SIZE / 2, - StartTime = 0, - }, - new HitCircle - { - Position = OsuPlayfield.BASE_SIZE / 2, - StartTime = 5000, - }, - }, - Breaks = - { - new BreakPeriod(2000, 3000) - } - }; + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null) + => new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(manualClock), Audio); [BackgroundDependencyLoader] private void load() @@ -63,103 +50,154 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods [Test] public void TestUserAlreadyHasTouchDeviceActive() { + loadPlayer(); // it is presumed that a previous screen (i.e. song select) will set this up AddStep("set up touchscreen user", () => { - Player.Score.ScoreInfo.Mods = Player.Score.ScoreInfo.Mods.Append(new OsuModTouchDevice()).ToArray(); + currentPlayer.Score.ScoreInfo.Mods = currentPlayer.Score.ScoreInfo.Mods.Append(new OsuModTouchDevice()).ToArray(); statics.SetValue(Static.TouchInputActive, true); }); - AddUntilStep("wait until 0 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0).Within(500)); - AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); - AddUntilStep("wait until 0", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); + + AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0)); + AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); AddStep("touch circle", () => { - var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); InputManager.BeginTouch(touch); InputManager.EndTouch(touch); }); - AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); } [Test] public void TestTouchDuringBreak() { - AddUntilStep("wait until 2000 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000).Within(500)); - AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); - AddUntilStep("wait until 2000", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000)); + loadPlayer(); + AddStep("seek to 2000", () => currentPlayer.GameplayClockContainer.Seek(2000)); + AddUntilStep("wait until 2000", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000)); AddStep("touch playfield", () => { - var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); InputManager.BeginTouch(touch); InputManager.EndTouch(touch); }); - AddAssert("touch device mod not activated", () => Player.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); + AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); } [Test] public void TestTouchMiss() { + loadPlayer(); // ensure mouse is active (and that it's not suppressed due to touches in previous tests) AddStep("click mouse", () => InputManager.Click(MouseButton.Left)); - AddUntilStep("wait until 200 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(200).Within(500)); - AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); - AddUntilStep("wait until 200", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(200)); + AddStep("seek to 200", () => currentPlayer.GameplayClockContainer.Seek(200)); + AddUntilStep("wait until 200", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(200)); AddStep("touch playfield", () => { - var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); InputManager.BeginTouch(touch); InputManager.EndTouch(touch); }); - AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); } [Test] public void TestIncompatibleModActive() { + loadPlayer(); // this is only a veneer of enabling autopilot as having it actually active from the start is annoying to make happen // given the tests' structure. - AddStep("enable autopilot", () => Player.Score.ScoreInfo.Mods = new Mod[] { new OsuModAutopilot() }); + AddStep("enable autopilot", () => currentPlayer.Score.ScoreInfo.Mods = new Mod[] { new OsuModAutopilot() }); - AddUntilStep("wait until 0 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0).Within(500)); - AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); - AddUntilStep("wait until 0", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); + AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0)); + AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); AddStep("touch playfield", () => { - var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); InputManager.BeginTouch(touch); InputManager.EndTouch(touch); }); - AddAssert("touch device mod not activated", () => Player.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); + AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); } [Test] public void TestSecondObjectTouched() { + loadPlayer(); // ensure mouse is active (and that it's not suppressed due to touches in previous tests) AddStep("click mouse", () => InputManager.Click(MouseButton.Left)); - AddUntilStep("wait until 0 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0).Within(500)); - AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); - AddUntilStep("wait until 0", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); + AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0)); + AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); AddStep("click circle", () => { - InputManager.MoveMouseTo(Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + InputManager.MoveMouseTo(currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); InputManager.Click(MouseButton.Left); }); - AddAssert("touch device mod not activated", () => Player.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); + AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf()); - AddStep("speed back up", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 1); - AddUntilStep("wait until 5000 near", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000).Within(500)); - AddStep("slow down", () => Player.GameplayClockContainer.AdjustmentsFromMods.Frequency.Value = 0.2); - AddUntilStep("wait until 5000", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000)); + AddStep("seek to 5000", () => currentPlayer.GameplayClockContainer.Seek(5000)); + AddUntilStep("wait until 5000", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000)); AddStep("touch playfield", () => { - var touch = new Touch(TouchSource.Touch1, Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); + var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); InputManager.BeginTouch(touch); InputManager.EndTouch(touch); }); - AddAssert("touch device mod activated", () => Player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + } + + private void loadPlayer() + { + AddStep("load player", () => + { + Beatmap.Value = CreateWorkingBeatmap(new OsuBeatmap + { + HitObjects = + { + new HitCircle + { + Position = OsuPlayfield.BASE_SIZE / 2, + StartTime = 0, + }, + new HitCircle + { + Position = OsuPlayfield.BASE_SIZE / 2, + StartTime = 5000, + }, + }, + Breaks = + { + new BreakPeriod(2000, 3000) + } + }); + + var p = new ScoreAccessibleSoloPlayer(); + + LoadScreen(currentPlayer = p); + }); + + AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); + AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); + } + + private partial class ScoreAccessibleSoloPlayer : SoloPlayer + { + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + + protected override bool PauseOnFocusLost => false; + + public ScoreAccessibleSoloPlayer() + : base(new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) + { + } } } } From 8690b0876928570e5df1d25a1568db6ec703ee05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 16:08:45 +0900 Subject: [PATCH 143/207] Fix compose select box test failures --- osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index 80c69aacf6..9e8d75efea 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -47,6 +47,7 @@ namespace osu.Game.Tests.Visual.Editing CanScaleX = true, CanScaleY = true, + CanScaleProportionally = true, CanFlipX = true, CanFlipY = true, From 60df2722ab7d72e040b176d0d756493c6c2daf4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 16:04:28 +0900 Subject: [PATCH 144/207] Rename `RoundedLine` to `BoxElement` and make more generically useful --- osu.Game/Skinning/ArgonSkin.cs | 4 +- osu.Game/Skinning/Components/BoxElement.cs | 50 +++++++++++++++++++++ osu.Game/Skinning/Components/RoundedLine.cs | 18 -------- 3 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Skinning/Components/BoxElement.cs delete mode 100644 osu.Game/Skinning/Components/RoundedLine.cs diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 03b089582d..b78abdffd7 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -116,7 +116,7 @@ namespace osu.Game.Skinning var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container => { var health = container.OfType().FirstOrDefault(); - var healthLine = container.OfType().FirstOrDefault(); + var healthLine = container.OfType().FirstOrDefault(); var scoreWedge = container.OfType().FirstOrDefault(); var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); @@ -213,7 +213,7 @@ namespace osu.Game.Skinning new ArgonScoreWedge(), new ArgonScoreCounter(), new ArgonHealthDisplay(), - new RoundedLine(), + new BoxElement(), new ArgonAccuracyCounter(), new ArgonComboCounter(), new BarHitErrorMeter(), diff --git a/osu.Game/Skinning/Components/BoxElement.cs b/osu.Game/Skinning/Components/BoxElement.cs new file mode 100644 index 0000000000..235f97ceef --- /dev/null +++ b/osu.Game/Skinning/Components/BoxElement.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 osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Configuration; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Skinning.Components +{ + public partial class BoxElement : CompositeDrawable, ISerialisableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [SettingSource("Corner rounding", "How round the corners of the box should be.")] + public BindableFloat CornerRounding { get; } = new BindableFloat(1) + { + Precision = 0.01f, + MinValue = 0, + MaxValue = 1, + }; + + public BoxElement() + { + Size = new Vector2(400, 80); + + InternalChildren = new Drawable[] + { + new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, + }; + + Masking = true; + } + + protected override void Update() + { + base.Update(); + + CornerRadius = CornerRounding.Value * Math.Min(DrawWidth, DrawHeight) * 0.5f; + } + } +} diff --git a/osu.Game/Skinning/Components/RoundedLine.cs b/osu.Game/Skinning/Components/RoundedLine.cs deleted file mode 100644 index 491f87d31e..0000000000 --- a/osu.Game/Skinning/Components/RoundedLine.cs +++ /dev/null @@ -1,18 +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.Graphics.Shapes; -using osuTK; - -namespace osu.Game.Skinning.Components -{ - public partial class RoundedLine : Circle, ISerialisableDrawable - { - public bool UsesFixedAnchor { get; set; } - - public RoundedLine() - { - Size = new Vector2(200, 8); - } - } -} From 7db14baed74277383e4ac567577bb08b3b2aeea5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 16:13:45 +0900 Subject: [PATCH 145/207] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 73cd239854..b47e2f1ca8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From b7938e78a0dc05f6847323058ef58f235e67324f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 16:30:21 +0900 Subject: [PATCH 146/207] Make sure test is in a break ...because apparently it may take a while to update the flag. --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index f528795125..cd51ccd751 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -75,6 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods loadPlayer(); AddStep("seek to 2000", () => currentPlayer.GameplayClockContainer.Seek(2000)); AddUntilStep("wait until 2000", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000)); + AddUntilStep("wait until break entered", () => currentPlayer.IsBreakTime.Value); AddStep("touch playfield", () => { var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre); From fa5921862f99e6ed4a9757938ad77a8bd9d066ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 16:19:42 +0900 Subject: [PATCH 147/207] Update deserialising test --- .../Archives/modified-argon-20231108.osk | Bin 1494 -> 1473 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk b/osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk index d07c5171007777784216e3ee8d4bc631a602bfb9..d56c4d4dcd8cfbfbdb536cd889dc72bdb5ff76f2 100644 GIT binary patch delta 1141 zcmcb{eUQ68z?+#xgn@y9gJEV{R`|Z@{*%Ge4FSv07GIlh~F@+%Q~oV{YfQ?8rI2O75pb4LXAg8B&@X=l+b z=Qalgdu>*@_;|N3u^G~KWK-~H()ze-kAzcx#5T%JAg zhc(lNfXSwe0YV`EU%Y-baWl~GSz-(fVhodWSS9M;h6m=~G7zYHzrLfQjl;?D^%A?1 z3u`Z%@UDIPEg*DSa^I1|H(tECuq^cdZ@tE#1(D0c6ib31GJd-Iv*zRXyEpgU{k$hi z{--FvXw5a{;Oh5xh1@ch_?!^6jem0f9@F{U^p_$hf{e{R+JEHyv7Cu<%UAZU<^N7r z-nTz=;>CP}o`iY@M~Az+%hy)hGpsfFwc+LS`@61tPw^JuGgaZ+xvIKZB_yb(@ynSW zNBLJSE2I7?!3FUkv?boS~)gGN(#>Dk(5 zhQ_n{3wMcs;OM%;a<=P$n#}Pndn2Yl%f1%%InajXYeKc~!^PX{_i;$;->&<1KO~}; z>$!OJ;>C=!I5I`cR_%6MZS&OhZ}W3*eT^K~dPmDcr(J~<=O5$F&6#+6PWYqL!bvNC zx!taK{YfAe&`{_{A=`0vMd@7;YLC9zNLVhQ9!&ES(?vS`SI zguwX#nAht;`5uVjpz-x&16CQZ8q|CNSHZy0*f=?xRTZp8W@=KE8c<3Ti20C|Ff?XN NUeBt?wu=QM2ml}T`)L3G delta 1146 zcmX@eeT}<5z?+#xgn@y9gP~P0BfMgN@{#>OUOFR4gn^+rJ2Ou&GcWUKzhT}X2N9Rw zwnvYMdTiwNW0|-+q$EVq*X3j4{&n05!$_KRIWI3VioSNzawdi2AtyJm(a(3BKBk7haZ<3wWnjo?buyeER+OkztP|UQS_? zvFpmdS&@8Kdges87`7?1zn}hdH|j+3-AXIA#hD&cZ^*ykc(Yl6v1JqcoMqom9^F{m zGEpRbp-cS_zQY`|t~F;~{>8RphDdwg-2bz}>>pY=T8K=@Sh6RRcT&UV55h~He_}hb z(8D6%M1C>C!ABHw-e^!(;id$#d> z;S5;!ICj->u=ZyIdqNv9I{;Dyu*TfgLlO zI0cTrd!SfQ`^VyJ(El?o^|h>j>=jNJFDesh(9P#P=~pnv3q~dpqO#f%wDP@6VQ(Z9i^v{GILGinpgeUBAcov1pQu$MKI%(&syJ z`&J)Jy?FjgZBg#uW$Ry-l-+q=ruF;IxnmOX_K#1US+COI#Np7qBCn&1c4?^_^XfdXNA8lsf-^ z{j)GheYq){_ulkJ$q186SpxY`v;E{NEE@74A*4LQzzfVPaM1X4vNWrV0!n5PV_*Ox k1Zb>goIICNVsbt!4>M5V Date: Fri, 10 Nov 2023 16:37:07 +0900 Subject: [PATCH 148/207] Remove width/height bindables from `ArgonWedgePiece` --- osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs | 24 +++----------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs b/osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs index 085e4c738f..3c2e3e05ea 100644 --- a/osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs +++ b/osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs @@ -18,30 +18,14 @@ namespace osu.Game.Screens.Play.HUD { public bool UsesFixedAnchor { get; set; } - public float EndOpacity { get; init; } = 0.25f; - - [SettingSource("Wedge width")] - public BindableFloat WedgeWidth { get; } = new BindableFloat(400f) - { - MinValue = 0f, - MaxValue = 500f, - Precision = 1f, - }; - - [SettingSource("Wedge height")] - public BindableFloat WedgeHeight { get; } = new BindableFloat(100f) - { - MinValue = 0f, - MaxValue = 500f, - Precision = 1f, - }; - [SettingSource("Inverted shear")] public BindableBool InvertShear { get; } = new BindableBool(); public ArgonWedgePiece() { CornerRadius = 10f; + + Size = new Vector2(400, 100); } [BackgroundDependencyLoader] @@ -53,7 +37,7 @@ namespace osu.Game.Screens.Play.HUD InternalChild = new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#66CCFF").Opacity(0.0f), Color4Extensions.FromHex("#66CCFF").Opacity(EndOpacity)), + Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#66CCFF").Opacity(0.0f), Color4Extensions.FromHex("#66CCFF").Opacity(0.25f)), }; } @@ -61,8 +45,6 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); - WedgeWidth.BindValueChanged(v => Width = v.NewValue, true); - WedgeHeight.BindValueChanged(v => Height = v.NewValue, true); InvertShear.BindValueChanged(v => Shear = new Vector2(0.8f, 0f) * (v.NewValue ? -1 : 1), true); } } From 67312a2db922287fef12c53f00be99b2aa79df8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 16:43:47 +0900 Subject: [PATCH 149/207] Remove `ArgonScoreWedge` and use `ArgonWedgePiece` directly --- osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs | 37 -------------------- osu.Game/Skinning/ArgonSkin.cs | 26 ++++++++------ 2 files changed, 16 insertions(+), 47 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs b/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs deleted file mode 100644 index e8ade9e5c6..0000000000 --- a/osu.Game/Screens/Play/HUD/ArgonScoreWedge.cs +++ /dev/null @@ -1,37 +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.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Skinning; -using osuTK; - -namespace osu.Game.Screens.Play.HUD -{ - public partial class ArgonScoreWedge : CompositeDrawable, ISerialisableDrawable - { - public bool UsesFixedAnchor { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - AutoSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new ArgonWedgePiece - { - WedgeWidth = { Value = 380 }, - WedgeHeight = { Value = 72 }, - }, - new ArgonWedgePiece - { - WedgeWidth = { Value = 380 }, - WedgeHeight = { Value = 72 }, - Position = new Vector2(4, 5) - }, - }; - } - } -} diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index b78abdffd7..ddb375778c 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -117,7 +117,7 @@ namespace osu.Game.Skinning { var health = container.OfType().FirstOrDefault(); var healthLine = container.OfType().FirstOrDefault(); - var scoreWedge = container.OfType().FirstOrDefault(); + var wedgePieces = container.OfType().ToArray(); var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); var combo = container.OfType().FirstOrDefault(); @@ -144,15 +144,13 @@ namespace osu.Game.Skinning healthLine.Size = new Vector2(45, 3); } - if (scoreWedge != null) - { - scoreWedge.Position = new Vector2(-50, 15); + foreach (var wedgePiece in wedgePieces) + wedgePiece.Position += new Vector2(-50, 15); - if (score != null) - { - score.Origin = Anchor.TopRight; - score.Position = new Vector2(components_x_offset + 200, scoreWedge.Y + 30); - } + if (score != null) + { + score.Origin = Anchor.TopRight; + score.Position = new Vector2(components_x_offset + 200, wedgePieces.Last().Y + 30); } if (accuracy != null) @@ -210,7 +208,15 @@ namespace osu.Game.Skinning { Children = new Drawable[] { - new ArgonScoreWedge(), + new ArgonWedgePiece + { + Size = new Vector2(380, 72), + }, + new ArgonWedgePiece + { + Size = new Vector2(380, 72), + Position = new Vector2(4, 5) + }, new ArgonScoreCounter(), new ArgonHealthDisplay(), new BoxElement(), From a02aeed50af48f580a1c5d7fbb8a792f2a652f1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 16:50:41 +0900 Subject: [PATCH 150/207] Adjust combo animation slightly --- osu.Game/Screens/Play/HUD/ArgonComboCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index 63a17529f5..ac710294ef 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play.HUD float newScale = Math.Clamp(text.NumberContainer.Scale.X * (wasIncrease ? 1.1f : 0.8f), 0.6f, 1.4f); - float duration = wasMiss ? 2000 : 300; + float duration = wasMiss ? 2000 : 500; text.NumberContainer .ScaleTo(new Vector2(newScale)) From 46a219e01010e32827f3772aae77fece7de8bccf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 17:53:02 +0900 Subject: [PATCH 151/207] Add comment explaining `AutoSize` change in `LegacySongProgress` --- osu.Game/Skinning/LegacySongProgress.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 26004ff111..fa6ee38fee 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -21,6 +21,8 @@ namespace osu.Game.Skinning public LegacySongProgress() { + // User shouldn't be able to adjust width/height of this as `CircularProgress` doesn't + // handle stretched cases ell. AutoSizeAxes = Axes.Both; } From f25489cc7be71426891f50046b70aefdc24578de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 17:53:56 +0900 Subject: [PATCH 152/207] Check X/Y sizing available separately to fix weird edge cases --- .../SkinEditor/SkinSelectionHandler.cs | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 77e522a925..a4d530a4d9 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -32,11 +32,11 @@ namespace osu.Game.Overlays.SkinEditor UpdatePosition = updateDrawablePosition }; - private bool allSelectedSupportManualSizing => SelectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes == Axes.None); + private bool allSelectedSupportManualSizing(Axes axis) => SelectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(axis) == false); public override bool HandleScale(Vector2 scale, Anchor anchor) { - bool adjustSize; + Axes adjustAxis; switch (anchor) { @@ -45,19 +45,25 @@ namespace osu.Game.Overlays.SkinEditor case Anchor.TopRight: case Anchor.BottomLeft: case Anchor.BottomRight: - adjustSize = false; + adjustAxis = Axes.Both; break; // for edges, adjust size. + // autosize elements can't be easily handled so just disable sizing for now. case Anchor.TopCentre: - case Anchor.CentreLeft: - case Anchor.CentreRight: case Anchor.BottomCentre: - // autosize elements can't be easily handled so just disable sizing for now. - if (!allSelectedSupportManualSizing) + if (!allSelectedSupportManualSizing(Axes.Y)) return false; - adjustSize = true; + adjustAxis = Axes.Y; + break; + + case Anchor.CentreLeft: + case Anchor.CentreRight: + if (!allSelectedSupportManualSizing(Axes.X)) + return false; + + adjustAxis = Axes.X; break; default: @@ -151,10 +157,20 @@ namespace osu.Game.Overlays.SkinEditor if (Precision.AlmostEquals(MathF.Abs(drawableItem.Rotation) % 180, 90)) currentScaledDelta = new Vector2(scaledDelta.Y, scaledDelta.X); - if (adjustSize) - drawableItem.Size *= currentScaledDelta; - else - drawableItem.Scale *= currentScaledDelta; + switch (adjustAxis) + { + case Axes.X: + drawableItem.Width *= currentScaledDelta.X; + break; + + case Axes.Y: + drawableItem.Height *= currentScaledDelta.Y; + break; + + case Axes.Both: + drawableItem.Scale *= currentScaledDelta; + break; + } } return true; @@ -203,7 +219,8 @@ namespace osu.Game.Overlays.SkinEditor { base.OnSelectionChanged(); - SelectionBox.CanScaleX = SelectionBox.CanScaleY = allSelectedSupportManualSizing; + SelectionBox.CanScaleX = allSelectedSupportManualSizing(Axes.X); + SelectionBox.CanScaleY = allSelectedSupportManualSizing(Axes.Y); SelectionBox.CanScaleProportionally = true; SelectionBox.CanFlipX = true; SelectionBox.CanFlipY = true; From d0f1326a63da2187c80c7429cf386bfb3856c8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 17:54:48 +0900 Subject: [PATCH 153/207] Fix formatting --- osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index 93e2583eb7..b38fb9153a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -125,7 +125,9 @@ namespace osu.Game.Tests.Visual.Online Masking = true, EdgeEffect = new EdgeEffectParameters { - Type = EdgeEffectType.Shadow, Radius = 1, Colour = Color4.Black.Opacity(0.2f), + Type = EdgeEffectType.Shadow, + Radius = 1, + Colour = Color4.Black.Opacity(0.2f), }, }; } From 35e11c7c63b83122a45f5f9820aa6909ffb892bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 17:51:30 +0900 Subject: [PATCH 154/207] Rename diagonal scale variable and update xmldoc --- .../Edit/OsuSelectionHandler.cs | 2 +- .../Editing/TestSceneComposeSelectBox.cs | 2 +- .../SkinEditor/SkinSelectionHandler.cs | 2 +- .../Edit/Compose/Components/SelectionBox.cs | 22 +++++++++++-------- osu.sln.DotSettings | 1 + 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 4da640a971..4765f615ce 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Edit SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0; SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0; - SelectionBox.CanScaleProportionally = SelectionBox.CanScaleX && SelectionBox.CanScaleY; + SelectionBox.CanScaleDiagonally = SelectionBox.CanScaleX && SelectionBox.CanScaleY; 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 9e8d75efea..f6637d0e80 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editing CanScaleX = true, CanScaleY = true, - CanScaleProportionally = true, + CanScaleDiagonally = true, CanFlipX = true, CanFlipY = true, diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index a4d530a4d9..52c012a15a 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -221,7 +221,7 @@ namespace osu.Game.Overlays.SkinEditor SelectionBox.CanScaleX = allSelectedSupportManualSizing(Axes.X); SelectionBox.CanScaleY = allSelectedSupportManualSizing(Axes.Y); - SelectionBox.CanScaleProportionally = true; + SelectionBox.CanScaleDiagonally = true; SelectionBox.CanFlipX = true; SelectionBox.CanFlipY = true; SelectionBox.CanReverse = false; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 0917867a61..0b16941bc4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool canScaleX; /// - /// Whether horizontal scaling support should be enabled. + /// Whether horizontal scaling (from the left or right edge) support should be enabled. /// public bool CanScaleX { @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool canScaleY; /// - /// Whether vertical scaling support should be enabled. + /// Whether vertical scaling (from the top or bottom edge) support should be enabled. /// public bool CanScaleY { @@ -91,19 +91,23 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - private bool canScaleProportionally; + private bool canScaleDiagonally; /// - /// Whether vertical scaling support should be enabled. + /// Whether diagonal scaling (from a corner) support should be enabled. /// - public bool CanScaleProportionally + /// + /// There are some cases where we only want to allow proportional resizing, and not allow + /// one or both explicit directions of scale. + /// + public bool CanScaleDiagonally { - get => canScaleProportionally; + get => canScaleDiagonally; set { - if (canScaleProportionally == value) return; + if (canScaleDiagonally == value) return; - canScaleProportionally = value; + canScaleDiagonally = value; recreate(); } } @@ -262,7 +266,7 @@ namespace osu.Game.Screens.Edit.Compose.Components }; if (CanScaleX) addXScaleComponents(); - if (CanScaleProportionally) addFullScaleComponents(); + if (CanScaleDiagonally) addFullScaleComponents(); if (CanScaleY) addYScaleComponents(); if (CanFlipX) addXFlipComponents(); if (CanFlipY) addYFlipComponents(); diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index c2778ca5b1..342bc8aa79 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -823,6 +823,7 @@ See the LICENCE file in the repository root for full licence text. True True True + True True True True From 36d0bae42dfe95481b3e20751d8f7032b925e9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 17:57:16 +0900 Subject: [PATCH 155/207] Restore mention of dependency on another ctor param --- osu.Game/Users/Drawables/UpdateableAvatar.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index b020e7fa63..21153ecfc3 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -54,7 +54,10 @@ namespace osu.Game.Users.Drawables /// /// The initial user to display. /// If set to true, hover/click sounds will play and clicking the avatar will open the user's profile. - /// If set to true, the user status panel will be displayed in the tooltip. + /// + /// If set to true, the user status panel will be displayed in the tooltip. + /// Only has an effect if is true. + /// /// Whether to show a default guest representation on null user (as opposed to nothing). public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUserPanelOnHover = false, bool showGuestOnNull = true) { From 2c1f304f3b4faaa645edb3c138b915ad351c9fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 18:13:36 +0900 Subject: [PATCH 156/207] Fix test failures due to fluctuations in needlessly-serialised automatic sizings --- osu.Game/Skinning/SerialisableDrawableExtensions.cs | 7 +++++-- osu.Game/Skinning/SerialisedDrawableInfo.cs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SerialisableDrawableExtensions.cs b/osu.Game/Skinning/SerialisableDrawableExtensions.cs index 0b44db9bdc..609dd9c8ab 100644 --- a/osu.Game/Skinning/SerialisableDrawableExtensions.cs +++ b/osu.Game/Skinning/SerialisableDrawableExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; @@ -19,8 +20,10 @@ namespace osu.Game.Skinning // todo: can probably make this better via deserialisation directly using a common interface. component.Position = drawableInfo.Position; component.Rotation = drawableInfo.Rotation; - if (drawableInfo.Size != Vector2.Zero && (component as CompositeDrawable)?.AutoSizeAxes == Axes.None) - component.Size = drawableInfo.Size; + if (drawableInfo.Width is float width && width != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.X) == false) + component.Width = width; + if (drawableInfo.Height is float height && height != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.Y) == false) + component.Height = height; component.Scale = drawableInfo.Scale; component.Anchor = drawableInfo.Anchor; component.Origin = drawableInfo.Origin; diff --git a/osu.Game/Skinning/SerialisedDrawableInfo.cs b/osu.Game/Skinning/SerialisedDrawableInfo.cs index 0705e91d6d..b2237acc5a 100644 --- a/osu.Game/Skinning/SerialisedDrawableInfo.cs +++ b/osu.Game/Skinning/SerialisedDrawableInfo.cs @@ -35,7 +35,9 @@ namespace osu.Game.Skinning public Vector2 Scale { get; set; } - public Vector2 Size { get; set; } + public float? Width { get; set; } + + public float? Height { get; set; } public Anchor Anchor { get; set; } @@ -64,7 +66,8 @@ namespace osu.Game.Skinning Position = component.Position; Rotation = component.Rotation; Scale = component.Scale; - Size = component.Size; + Height = component.Height; + Width = component.Width; Anchor = component.Anchor; Origin = component.Origin; From 374e13b496dc3780e03f2c9f76a850ab0a8050e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 18:18:45 +0900 Subject: [PATCH 157/207] Remove argon health display bar length setting It is no longer needed. Intentionally not doing backwards migration to simplify things; users can fix their skins. --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 793d43f7ef..b4002343d3 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -35,14 +35,6 @@ namespace osu.Game.Screens.Play.HUD Precision = 1 }; - [SettingSource("Bar length")] - public BindableFloat BarLength { get; } = new BindableFloat(0.98f) - { - MinValue = 0.2f, - MaxValue = 1, - Precision = 0.01f, - }; - private BarPath mainBar = null!; /// @@ -140,7 +132,6 @@ namespace osu.Game.Screens.Play.HUD Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true); - BarLength.BindValueChanged(l => Width = l.NewValue, true); BarHeight.BindValueChanged(_ => updatePath()); updatePath(); } From 43a4b34295471a2a4c0103aa9383ef75cbb8dc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 18:20:14 +0900 Subject: [PATCH 158/207] Fix typo --- osu.Game/Skinning/LegacySongProgress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index fa6ee38fee..4295060a3a 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -22,7 +22,7 @@ namespace osu.Game.Skinning public LegacySongProgress() { // User shouldn't be able to adjust width/height of this as `CircularProgress` doesn't - // handle stretched cases ell. + // handle stretched cases well. AutoSizeAxes = Axes.Both; } From 26eae0bdeeed6670fc3a47b81abdc75afceda434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 18:25:39 +0900 Subject: [PATCH 159/207] Remove unused using directive --- osu.Game/Skinning/SerialisableDrawableExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/SerialisableDrawableExtensions.cs b/osu.Game/Skinning/SerialisableDrawableExtensions.cs index 609dd9c8ab..6938ed4091 100644 --- a/osu.Game/Skinning/SerialisableDrawableExtensions.cs +++ b/osu.Game/Skinning/SerialisableDrawableExtensions.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Extensions; -using osuTK; namespace osu.Game.Skinning { From ec2200d54fff1cf823355571c2a3c827c3a19ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 18:26:08 +0900 Subject: [PATCH 160/207] Remove another reference to bar length --- .../Visual/Gameplay/TestSceneArgonHealthDisplay.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index 7bad623d7f..f51577bc84 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -47,12 +47,6 @@ namespace osu.Game.Tests.Visual.Gameplay }; }); - AddSliderStep("Width", 0, 1f, 1f, val => - { - if (healthDisplay.IsNotNull()) - healthDisplay.BarLength.Value = val; - }); - AddSliderStep("Height", 0, 64, 0, val => { if (healthDisplay.IsNotNull()) From fbf94214a536a5b5aa7246d2ee34ee5ecacf8fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 18:36:09 +0900 Subject: [PATCH 161/207] Fully delegate tooltip show/hide logic --- osu.Game/Users/Drawables/ClickableAvatar.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 6390acc608..ef451df95d 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -105,17 +105,17 @@ namespace osu.Game.Users.Drawables public partial class NoCardTooltip : VisibilityContainer, ITooltip { + private readonly OsuTooltipContainer.OsuTooltip tooltip; + public NoCardTooltip() { - var tooltip = new OsuTooltipContainer.OsuTooltip(); + tooltip = new OsuTooltipContainer.OsuTooltip(); tooltip.SetContent(ContextMenuStrings.ViewProfile); - tooltip.Show(); - Child = tooltip; } - protected override void PopIn() => this.FadeIn(150, Easing.OutQuint); - protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint); + protected override void PopIn() => tooltip.Show(); + protected override void PopOut() => tooltip.Hide(); public void Move(Vector2 pos) => Position = pos; From b6dcd7d55f0d421d85e2e97d00ae5704d1fb5487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 18:48:45 +0900 Subject: [PATCH 162/207] Fix tests so that they actually assert something --- .../Online/TestSceneUserClickableAvatar.cs | 67 +++---------------- 1 file changed, 9 insertions(+), 58 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index b38fb9153a..9edaa841b2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Testing; +using osu.Game.Graphics.Cursor; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; using osu.Game.Users.Drawables; @@ -41,65 +42,15 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestClickableAvatarHover() { - AddStep($"click user {1} with UserGridPanel {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 1) - return; + AddStep("hover avatar with user panel", () => InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(1))); + AddUntilStep("wait for tooltip to show", () => this.ChildrenOfType().FirstOrDefault()?.State.Value == Visibility.Visible); + AddStep("hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddUntilStep("wait for tooltip to hide", () => this.ChildrenOfType().FirstOrDefault()?.State.Value == Visibility.Hidden); - InputManager.MoveMouseTo(targets[0]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click user {2} with username only. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 2) - return; - - InputManager.MoveMouseTo(targets[1]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click user {3} with UserGridPanel {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 3) - return; - - InputManager.MoveMouseTo(targets[2]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click null user {4}. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 4) - return; - - InputManager.MoveMouseTo(targets[3]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); - - AddStep($"click null user {5}. {nameof(ClickableAvatar)}", () => - { - var targets = this.ChildrenOfType().ToList(); - if (targets.Count < 5) - return; - - InputManager.MoveMouseTo(targets[4]); - }); - AddWaitStep("wait for tooltip to show", 5); - AddStep("Hover out", () => InputManager.MoveMouseTo(new Vector2(0))); - AddWaitStep("wait for tooltip to hide", 3); + AddStep("hover avatar without user panel", () => InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(0))); + AddUntilStep("wait for tooltip to show", () => this.ChildrenOfType().FirstOrDefault()?.State.Value == Visibility.Visible); + AddStep("hover out", () => InputManager.MoveMouseTo(new Vector2(0))); + AddUntilStep("wait for tooltip to hide", () => this.ChildrenOfType().FirstOrDefault()?.State.Value == Visibility.Hidden); } private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool showPanel, string? color = null) From b7acbde719744a3aac166c676b792e9d87699612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Nov 2023 19:12:26 +0900 Subject: [PATCH 163/207] Only store width/height of serialised drawable if it isn't automatically computed --- osu.Game/Skinning/SerialisedDrawableInfo.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SerialisedDrawableInfo.cs b/osu.Game/Skinning/SerialisedDrawableInfo.cs index b2237acc5a..2d6113ff70 100644 --- a/osu.Game/Skinning/SerialisedDrawableInfo.cs +++ b/osu.Game/Skinning/SerialisedDrawableInfo.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; @@ -66,8 +67,13 @@ namespace osu.Game.Skinning Position = component.Position; Rotation = component.Rotation; Scale = component.Scale; - Height = component.Height; - Width = component.Width; + + if ((component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.X) != true) + Width = component.Width; + + if ((component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.Y) != true) + Height = component.Height; + Anchor = component.Anchor; Origin = component.Origin; From 57cd5194ce06b16e03dc0dea32a0e81da80b1029 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 20:00:20 +0900 Subject: [PATCH 164/207] Flip comparison to allow non-composite drawables to still get resized --- osu.Game/Skinning/SerialisableDrawableExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SerialisableDrawableExtensions.cs b/osu.Game/Skinning/SerialisableDrawableExtensions.cs index 6938ed4091..97c4cc8f73 100644 --- a/osu.Game/Skinning/SerialisableDrawableExtensions.cs +++ b/osu.Game/Skinning/SerialisableDrawableExtensions.cs @@ -19,9 +19,9 @@ namespace osu.Game.Skinning // todo: can probably make this better via deserialisation directly using a common interface. component.Position = drawableInfo.Position; component.Rotation = drawableInfo.Rotation; - if (drawableInfo.Width is float width && width != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.X) == false) + if (drawableInfo.Width is float width && width != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.X) != true) component.Width = width; - if (drawableInfo.Height is float height && height != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.Y) == false) + if (drawableInfo.Height is float height && height != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.Y) != true) component.Height = height; component.Scale = drawableInfo.Scale; component.Anchor = drawableInfo.Anchor; From 2e48569982039c9c1e6cb722190a6669ea09aee7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2023 20:00:51 +0900 Subject: [PATCH 165/207] Improve test comparison logic Doesn't really help that much with nunit output, but at least you can breakpoint and see the json kinda. --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 4e5db5d46e..bd56a95809 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -4,6 +4,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; +using System.Text.Unicode; +using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; @@ -214,7 +217,11 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddStep("Press undo", () => InputManager.Keys(PlatformAction.Undo)); - AddAssert("Nothing changed", () => defaultState.SequenceEqual(changeHandler.GetCurrentState())); + + AddAssert("Nothing changed", + () => JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(defaultState)), + () => Is.EqualTo(JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(changeHandler.GetCurrentState()))) + ); AddStep("Add components", () => { @@ -243,7 +250,11 @@ namespace osu.Game.Tests.Visual.Gameplay void revertAndCheckUnchanged() { AddStep("Revert changes", () => changeHandler.RestoreState(int.MinValue)); - AddAssert("Current state is same as default", () => defaultState.SequenceEqual(changeHandler.GetCurrentState())); + + AddAssert("Current state is same as default", + () => JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(defaultState)), + () => Is.EqualTo(JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(changeHandler.GetCurrentState()))) + ); } } From 080f13e34d0f5f60381d813edfd590aa2b466194 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 11 Nov 2023 02:56:16 +0300 Subject: [PATCH 166/207] Schedule outside of `UnloadStoryboard` and fix disposal happening on update thread --- .../Backgrounds/BeatmapBackgroundWithStoryboard.cs | 13 +++++-------- osu.Game/Screens/BackgroundScreenStack.cs | 2 +- .../Screens/Backgrounds/BackgroundScreenDefault.cs | 13 +++++++++++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 1e702967b6..2bde71a6a1 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -71,7 +71,7 @@ namespace osu.Game.Graphics.Backgrounds }, (loadCancellationSource = new CancellationTokenSource()).Token); } - public void UnloadStoryboard(Action scheduleStoryboardRemoval) + public void UnloadStoryboard() { if (drawableStoryboard == null) return; @@ -79,15 +79,12 @@ namespace osu.Game.Graphics.Backgrounds loadCancellationSource.AsNonNull().Cancel(); loadCancellationSource = null; - DrawableStoryboard s = drawableStoryboard; - - scheduleStoryboardRemoval(() => - { - s.RemoveAndDisposeImmediately(); - Sprite.Alpha = 1f; - }); + // clear is intentionally used here for the storyboard to be disposed asynchronously. + storyboardContainer.Clear(); drawableStoryboard = null; + + Sprite.Alpha = 1f; } protected override void LoadComplete() diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 9af6601aa4..2c7b219791 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -35,6 +35,6 @@ namespace osu.Game.Screens return true; } - internal void ScheduleToTransitionEnd(Action action) => Scheduler.AddDelayed(action, BackgroundScreen.TRANSITION_LENGTH); + internal ScheduledDelegate ScheduleUntilTransitionEnd(Action action) => Scheduler.AddDelayed(action, BackgroundScreen.TRANSITION_LENGTH); } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 07b1cc6df4..4583b3e4d6 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -73,13 +73,15 @@ namespace osu.Game.Screens.Backgrounds void next() => Next(); } + private ScheduledDelegate storyboardUnloadDelegate; + public override void OnSuspending(ScreenTransitionEvent e) { var backgroundScreenStack = Parent as BackgroundScreenStack; Debug.Assert(backgroundScreenStack != null); if (background is BeatmapBackgroundWithStoryboard storyboardBackground) - storyboardBackground.UnloadStoryboard(backgroundScreenStack.ScheduleToTransitionEnd); + storyboardUnloadDelegate = backgroundScreenStack.ScheduleUntilTransitionEnd(storyboardBackground.UnloadStoryboard); base.OnSuspending(e); } @@ -87,7 +89,14 @@ namespace osu.Game.Screens.Backgrounds public override void OnResuming(ScreenTransitionEvent e) { if (background is BeatmapBackgroundWithStoryboard storyboardBackground) - storyboardBackground.LoadStoryboard(); + { + if (storyboardUnloadDelegate?.Completed == false) + storyboardUnloadDelegate.Cancel(); + else + storyboardBackground.LoadStoryboard(); + + storyboardUnloadDelegate = null; + } base.OnResuming(e); } From bb912bc6161a8785f105e3da29f7e95f83261249 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 11 Nov 2023 02:57:17 +0300 Subject: [PATCH 167/207] Avoid spinning another load thread on initial storyboard load --- .../BeatmapBackgroundWithStoryboard.cs | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 2bde71a6a1..784c8e4b44 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -1,13 +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 System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; @@ -48,27 +46,37 @@ namespace osu.Game.Graphics.Backgrounds Volume = { Value = 0 }, }); - LoadStoryboard(); + LoadStoryboard(false); } - public void LoadStoryboard() + public void LoadStoryboard(bool async = true) { Debug.Assert(drawableStoryboard == null); if (!Beatmap.Storyboard.HasDrawable) return; - LoadComponentAsync(drawableStoryboard = new DrawableStoryboard(Beatmap.Storyboard, mods.Value) + drawableStoryboard = new DrawableStoryboard(Beatmap.Storyboard, mods.Value) { Clock = storyboardClock - }, s => + }; + + if (async) + LoadComponentAsync(drawableStoryboard, finishLoad, (loadCancellationSource = new CancellationTokenSource()).Token); + else + { + LoadComponent(drawableStoryboard); + finishLoad(drawableStoryboard); + } + + void finishLoad(DrawableStoryboard s) { if (Beatmap.Storyboard.ReplacesBackground) Sprite.FadeOut(BackgroundScreen.TRANSITION_LENGTH, Easing.InQuint); storyboardContainer.FadeInFromZero(BackgroundScreen.TRANSITION_LENGTH, Easing.OutQuint); storyboardContainer.Add(s); - }, (loadCancellationSource = new CancellationTokenSource()).Token); + } } public void UnloadStoryboard() @@ -76,7 +84,7 @@ namespace osu.Game.Graphics.Backgrounds if (drawableStoryboard == null) return; - loadCancellationSource.AsNonNull().Cancel(); + loadCancellationSource?.Cancel(); loadCancellationSource = null; // clear is intentionally used here for the storyboard to be disposed asynchronously. From 96da7a07bbd79bc67cbc58196bb38cd6c835c83a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 11 Nov 2023 02:57:29 +0300 Subject: [PATCH 168/207] Add detailed explaination on existence of `ScheduleUntilTransitionEnd` --- osu.Game/Screens/BackgroundScreenStack.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 2c7b219791..562b212561 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Screens; +using osu.Framework.Threading; +using osu.Game.Screens.Backgrounds; namespace osu.Game.Screens { @@ -35,6 +37,19 @@ namespace osu.Game.Screens return true; } + /// + /// Schedules a delegate to run after 500ms, the time length of a background screen transition. + /// This is used in to dispose of the storyboard once the background screen is completely off-screen. + /// + /// + /// Late storyboard disposals cannot be achieved with any local scheduler from or any component inside it, + /// due to the screen becoming dead at the moment the transition finishes. And, on the frame that it is dead on, it will not receive an , + /// therefore not guaranteeing to dispose the storyboard at any period of time close to the end of the transition. + /// This might require reconsideration framework-side, possibly exposing a "death" event in or all s in general. + /// + /// The delegate + /// + /// internal ScheduledDelegate ScheduleUntilTransitionEnd(Action action) => Scheduler.AddDelayed(action, BackgroundScreen.TRANSITION_LENGTH); } } From 21e1d68b15960c448d9876baaec51776febde22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Nov 2023 15:52:24 +0900 Subject: [PATCH 169/207] Remove unused using directive --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index bd56a95809..08019c90e1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using System.Text.Unicode; using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.Allocation; From 2428a97d449b5e5f51ea2f9c8805c32521301a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Nov 2023 17:02:21 +0900 Subject: [PATCH 170/207] Fix editor not clearing undo history on skin change --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 5affaedf1d..78c1ea2f0b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -406,7 +406,14 @@ namespace osu.Game.Overlays.SkinEditor cp.Colour = colours.Yellow; }); + changeHandler?.Dispose(); + skins.EnsureMutableSkin(); + + var targetContainer = getTarget(selectedTarget.Value); + + if (targetContainer != null) + changeHandler = new SkinEditorChangeHandler(targetContainer); hasBegunMutating = true; } From b247b94ecbdb5405de16ba489ef7a62b36cd7656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Nov 2023 19:15:20 +0900 Subject: [PATCH 171/207] Revert test changes They were broken because `SerialisedDrawableInfo` is a reference type and as such equality checks will use reference equality rather than member equality. --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 08019c90e1..4e5db5d46e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -4,8 +4,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; @@ -216,11 +214,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddStep("Press undo", () => InputManager.Keys(PlatformAction.Undo)); - - AddAssert("Nothing changed", - () => JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(defaultState)), - () => Is.EqualTo(JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(changeHandler.GetCurrentState()))) - ); + AddAssert("Nothing changed", () => defaultState.SequenceEqual(changeHandler.GetCurrentState())); AddStep("Add components", () => { @@ -249,11 +243,7 @@ namespace osu.Game.Tests.Visual.Gameplay void revertAndCheckUnchanged() { AddStep("Revert changes", () => changeHandler.RestoreState(int.MinValue)); - - AddAssert("Current state is same as default", - () => JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(defaultState)), - () => Is.EqualTo(JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(changeHandler.GetCurrentState()))) - ); + AddAssert("Current state is same as default", () => defaultState.SequenceEqual(changeHandler.GetCurrentState())); } } From 61d336521da4adc3c8dcc6bacc20a7f23a306d34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Nov 2023 19:48:56 +0900 Subject: [PATCH 172/207] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2870696c03..15553510cb 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index f1159f58b9..ef54dd06b4 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From b0aa4a4257a5f590a1b65613cbf988b7034d7a9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 Nov 2023 20:34:35 +0900 Subject: [PATCH 173/207] Add "export" item to skin editor menu --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 78c1ea2f0b..38eed55241 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -153,6 +154,8 @@ namespace osu.Game.Overlays.SkinEditor Items = new[] { new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), + new EditorMenuItem(CommonStrings.Export, MenuItemType.Standard, () => skins.ExportCurrentSkin()) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, + new EditorMenuItemSpacer(), new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new RevertConfirmDialog(revert))), new EditorMenuItemSpacer(), new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()), From 870e4ce27e751d2a7129d5fe396f2f96e74865bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Nov 2023 20:42:45 +0900 Subject: [PATCH 174/207] Fix argon health display not handling invalidation correctly --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index b56920c99a..f4ce7d1633 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -89,6 +89,13 @@ namespace osu.Game.Screens.Play.HUD public const float MAIN_PATH_RADIUS = 10f; + private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); + + public ArgonHealthDisplay() + { + AddLayout(drawSizeLayout); + } + [BackgroundDependencyLoader] private void load() { @@ -134,22 +141,11 @@ namespace osu.Game.Screens.Play.HUD Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true); - UseRelativeSize.BindValueChanged(v => - { - RelativeSizeAxes = v.NewValue ? Axes.X : Axes.None; - }, true); + UseRelativeSize.BindValueChanged(v => RelativeSizeAxes = v.NewValue ? Axes.X : Axes.None, true); BarHeight.BindValueChanged(_ => updatePath(), true); } - protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) - { - if ((invalidation & Invalidation.DrawSize) > 0) - updatePath(); - - return base.OnInvalidate(invalidation, source); - } - private void updateCurrent() { if (Current.Value >= GlowBarValue) finishMissDisplay(); @@ -165,6 +161,12 @@ namespace osu.Game.Screens.Play.HUD { base.Update(); + if (!drawSizeLayout.IsValid) + { + updatePath(); + drawSizeLayout.Validate(); + } + mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed); glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); } From ee56b4d205f916ea9cf45543c72cce34f53b8a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Nov 2023 20:55:47 +0900 Subject: [PATCH 175/207] Use barely better assertion fail message in test --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 4e5db5d46e..92f28288ca 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; @@ -243,7 +244,9 @@ namespace osu.Game.Tests.Visual.Gameplay void revertAndCheckUnchanged() { AddStep("Revert changes", () => changeHandler.RestoreState(int.MinValue)); - AddAssert("Current state is same as default", () => defaultState.SequenceEqual(changeHandler.GetCurrentState())); + AddAssert("Current state is same as default", + () => Encoding.UTF8.GetString(defaultState), + () => Is.EqualTo(Encoding.UTF8.GetString(changeHandler.GetCurrentState()))); } } From 50789d2e18a43dd3b2a2e99ea9487851972f35f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Nov 2023 21:28:04 +0900 Subject: [PATCH 176/207] Fix song progress bars not sizing vertically properly --- .../Screens/Play/HUD/ArgonSongProgress.cs | 66 +++++++++++-------- .../Screens/Play/HUD/DefaultSongProgress.cs | 49 ++++++++------ 2 files changed, 65 insertions(+), 50 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonSongProgress.cs b/osu.Game/Screens/Play/HUD/ArgonSongProgress.cs index be2ce3b272..cb38854bca 100644 --- a/osu.Game/Screens/Play/HUD/ArgonSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/ArgonSongProgress.cs @@ -19,6 +19,7 @@ namespace osu.Game.Screens.Play.HUD private readonly ArgonSongProgressGraph graph; private readonly ArgonSongProgressBar bar; private readonly Container graphContainer; + private readonly Container content; private const float bar_height = 10; @@ -30,43 +31,50 @@ namespace osu.Game.Screens.Play.HUD public ArgonSongProgress() { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; Masking = true; CornerRadius = 5; - Children = new Drawable[] + + Child = content = new Container { - info = new SongProgressInfo + RelativeSizeAxes = Axes.X, + Children = new Drawable[] { - Origin = Anchor.TopLeft, - Name = "Info", - Anchor = Anchor.TopLeft, - RelativeSizeAxes = Axes.X, - ShowProgress = false - }, - bar = new ArgonSongProgressBar(bar_height) - { - Name = "Seek bar", - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - OnSeek = time => player?.Seek(time), - }, - graphContainer = new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Masking = true, - CornerRadius = 5, - Child = graph = new ArgonSongProgressGraph + info = new SongProgressInfo { - Name = "Difficulty graph", - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive + Origin = Anchor.TopLeft, + Name = "Info", + Anchor = Anchor.TopLeft, + RelativeSizeAxes = Axes.X, + ShowProgress = false }, - RelativeSizeAxes = Axes.X, - }, + bar = new ArgonSongProgressBar(bar_height) + { + Name = "Seek bar", + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + OnSeek = time => player?.Seek(time), + }, + graphContainer = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Masking = true, + CornerRadius = 5, + Child = graph = new ArgonSongProgressGraph + { + Name = "Difficulty graph", + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive + }, + RelativeSizeAxes = Axes.X, + }, + } }; - RelativeSizeAxes = Axes.X; } [BackgroundDependencyLoader] @@ -100,7 +108,7 @@ namespace osu.Game.Screens.Play.HUD protected override void Update() { base.Update(); - Height = bar.Height + bar_height + info.Height; + content.Height = bar.Height + bar_height + info.Height; graphContainer.Height = bar.Height; } diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 202ead2d66..48809796f3 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; @@ -27,6 +28,7 @@ namespace osu.Game.Screens.Play.HUD private readonly DefaultSongProgressBar bar; private readonly DefaultSongProgressGraph graph; private readonly SongProgressInfo info; + private readonly Container content; [SettingSource(typeof(SongProgressStrings), nameof(SongProgressStrings.ShowGraph), nameof(SongProgressStrings.ShowGraphDescription))] public Bindable ShowGraph { get; } = new BindableBool(true); @@ -37,31 +39,36 @@ namespace osu.Game.Screens.Play.HUD public DefaultSongProgress() { RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; Anchor = Anchor.BottomRight; Origin = Anchor.BottomRight; - Children = new Drawable[] + Child = content = new Container { - info = new SongProgressInfo + RelativeSizeAxes = Axes.X, + Children = new Drawable[] { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - }, - graph = new DefaultSongProgressGraph - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Height = graph_height, - Margin = new MarginPadding { Bottom = bottom_bar_height }, - }, - bar = new DefaultSongProgressBar(bottom_bar_height, graph_height, handle_size) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - OnSeek = time => player?.Seek(time), - }, + info = new SongProgressInfo + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + }, + graph = new DefaultSongProgressGraph + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Height = graph_height, + Margin = new MarginPadding { Bottom = bottom_bar_height }, + }, + bar = new DefaultSongProgressBar(bottom_bar_height, graph_height, handle_size) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + OnSeek = time => player?.Seek(time), + }, + } }; } @@ -107,7 +114,7 @@ namespace osu.Game.Screens.Play.HUD float newHeight = bottom_bar_height + graph_height + handle_size.Y + info.Height - graph.Y; if (!Precision.AlmostEquals(Height, newHeight, 5f)) - Height = newHeight; + content.Height = newHeight; } private void updateBarVisibility() From 3e8c89e282c74874a8c6885228910d70b64d2e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Nov 2023 07:42:41 +0900 Subject: [PATCH 177/207] Fix one more reference to removed setting --- .../Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index e9092bba1b..15a7b48323 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f), BarLength = { Value = 1f } }; + protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f), Width = 1f }; protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay { Scale = new Vector2(0.6f) }; protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay { Scale = new Vector2(0.6f) }; From 798e677c092a4ad02e0fe0a81163c14b57ff90d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 12 Nov 2023 15:12:04 +0900 Subject: [PATCH 178/207] Refactor `KeyCounterDisplay` to use autosize A previous attempt at this was unsuccessful due to a partially off-screen elements not getting the correct size early enough (see https://github.com/ppy/osu/issues/14793). This can be accounted for by setting `AlwaysPresent` when visibility is expected. This fixes [test failures](https://github.com/ppy/osu/actions/runs/6838444698/job/18595535795) due to the newly added `Width` / `Height` being persisted with floating-point errors (by not persisting the values in the first place, via `AutoSize.Both`). --- osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs | 12 ------------ .../Play/HUD/DefaultKeyCounterDisplay.cs | 14 -------------- osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs | 17 ++++++++++++++++- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs index 984c2a7287..44b90fcad0 100644 --- a/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs @@ -10,8 +10,6 @@ namespace osu.Game.Screens.Play { public partial class ArgonKeyCounterDisplay : KeyCounterDisplay { - private const int duration = 100; - protected override FillFlowContainer KeyFlow { get; } public ArgonKeyCounterDisplay() @@ -25,16 +23,6 @@ namespace osu.Game.Screens.Play }; } - protected override void Update() - { - base.Update(); - - Size = KeyFlow.Size; - } - protected override KeyCounter CreateCounter(InputTrigger trigger) => new ArgonKeyCounter(trigger); - - protected override void UpdateVisibility() - => KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); } } diff --git a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs index e459574243..e0f96d32bc 100644 --- a/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultKeyCounterDisplay.cs @@ -10,7 +10,6 @@ namespace osu.Game.Screens.Play.HUD { public partial class DefaultKeyCounterDisplay : KeyCounterDisplay { - private const int duration = 100; private const double key_fade_time = 80; protected override FillFlowContainer KeyFlow { get; } @@ -25,15 +24,6 @@ namespace osu.Game.Screens.Play.HUD }; } - protected override void Update() - { - base.Update(); - - // Don't use autosize as it will shrink to zero when KeyFlow is hidden. - // In turn this can cause the display to be masked off screen and never become visible again. - Size = KeyFlow.Size; - } - protected override KeyCounter CreateCounter(InputTrigger trigger) => new DefaultKeyCounter(trigger) { FadeTime = key_fade_time, @@ -41,10 +31,6 @@ namespace osu.Game.Screens.Play.HUD KeyUpTextColor = KeyUpTextColor, }; - protected override void UpdateVisibility() => - // Isolate changing visibility of the key counters from fading this component. - KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration); - private Color4 keyDownTextColor = Color4.DarkGray; public Color4 KeyDownTextColor diff --git a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs index e7e866932e..0a5d6b763e 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounterDisplay.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Rulesets.UI; @@ -31,13 +32,27 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private InputCountController controller { get; set; } = null!; - protected abstract void UpdateVisibility(); + private const int duration = 100; + + protected void UpdateVisibility() + { + bool visible = AlwaysVisible.Value || ConfigVisibility.Value; + + // Isolate changing visibility of the key counters from fading this component. + KeyFlow.FadeTo(visible ? 1 : 0, duration); + + // Ensure a valid size is immediately obtained even if partially off-screen + // See https://github.com/ppy/osu/issues/14793. + KeyFlow.AlwaysPresent = visible; + } protected abstract KeyCounter CreateCounter(InputTrigger trigger); [BackgroundDependencyLoader] private void load(OsuConfigManager config, DrawableRuleset? drawableRuleset) { + AutoSizeAxes = Axes.Both; + config.BindWith(OsuSetting.KeyOverlay, ConfigVisibility); if (drawableRuleset != null) From 31feeb5ddc616bb437c052b2b1680070c9d72563 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 12 Nov 2023 17:21:17 +0900 Subject: [PATCH 179/207] Disable new rider EAP inspection in test class --- osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs index 93005271a9..d0a45856b2 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs @@ -203,6 +203,7 @@ namespace osu.Game.Tests.Visual.Ranking public IBeatmap Beatmap { get; } + // ReSharper disable once NotNullOrRequiredMemberIsNotInitialized public TestBeatmapConverter(IBeatmap beatmap) { Beatmap = beatmap; From 469b9e2546a0eea247f19bdc9e793170fe7c7b05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 12 Nov 2023 17:28:15 +0900 Subject: [PATCH 180/207] Increase size and adjust positioning of max combo display in argon skin --- osu.Game/Skinning/ArgonSkin.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 715d5e4600..637467c748 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -182,15 +182,14 @@ namespace osu.Game.Skinning if (songProgress != null) { const float padding = 10; + // Hard to find this at runtime, so taken from the most expanded state during replay. + const float song_progress_offset_height = 36 + padding; songProgress.Position = new Vector2(0, -padding); songProgress.Scale = new Vector2(0.9f, 1); if (keyCounter != null && hitError != null) { - // Hard to find this at runtime, so taken from the most expanded state during replay. - const float song_progress_offset_height = 36 + padding; - keyCounter.Anchor = Anchor.BottomRight; keyCounter.Origin = Anchor.BottomRight; keyCounter.Position = new Vector2(-(hitError.Width + padding), -(padding * 2 + song_progress_offset_height)); @@ -200,7 +199,7 @@ namespace osu.Game.Skinning { combo.Anchor = Anchor.BottomLeft; combo.Origin = Anchor.BottomLeft; - combo.Position = new Vector2(hitError.Width + padding, -50); + combo.Position = new Vector2((hitError.Width + padding), -(padding * 2 + song_progress_offset_height)); } } } @@ -221,7 +220,10 @@ namespace osu.Game.Skinning new ArgonHealthDisplay(), new BoxElement(), new ArgonAccuracyCounter(), - new ArgonComboCounter(), + new ArgonComboCounter + { + Scale = new Vector2(1.3f) + }, new BarHitErrorMeter(), new BarHitErrorMeter(), new ArgonSongProgress(), From 69c2c0e4278d4d419b3ee1ea11f4c8e491514247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 Nov 2023 16:30:08 +0900 Subject: [PATCH 181/207] Fix touch device mod declared valid for multiplayer Mostly matters for web, so that it doesn't permit creation of playlist items with touch device inside. --- osu.Game/Rulesets/Mods/ModTouchDevice.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModTouchDevice.cs b/osu.Game/Rulesets/Mods/ModTouchDevice.cs index b80b042f11..e91a398700 100644 --- a/osu.Game/Rulesets/Mods/ModTouchDevice.cs +++ b/osu.Game/Rulesets/Mods/ModTouchDevice.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Mods public sealed override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen."; public sealed override double ScoreMultiplier => 1; public sealed override ModType Type => ModType.System; + public sealed override bool ValidForMultiplayer => false; + public sealed override bool ValidForMultiplayerAsFreeMod => false; public sealed override bool AlwaysValidForSubmission => true; public override Type[] IncompatibleMods => new[] { typeof(ICreateReplayData) }; } From 70d2de56695800a2ca14a027d32cf2c36153e8f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 Nov 2023 16:35:16 +0900 Subject: [PATCH 182/207] Move song select touch detector to solo implementation It should not run in multiplayer. Even if we wanted to allow touch-only playlist items at some point, the current behaviour of multiplayer song selects with respect to touch device mod is currently just broken. --- osu.Game/Screens/Select/PlaySongSelect.cs | 2 ++ osu.Game/Screens/Select/SongSelect.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index fe13d6d5a8..86bebdc2ff 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -48,6 +48,8 @@ namespace osu.Game.Screens.Select private void load(OsuColour colours) { BeatmapOptions.AddButton(ButtonSystemStrings.Edit.ToSentence(), @"beatmap", FontAwesome.Solid.PencilAlt, colours.Yellow, () => Edit()); + + AddInternal(new SongSelectTouchInputDetector()); } protected void PresentScore(ScoreInfo score) => diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 03083672d5..dfea4e3794 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -279,7 +279,6 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, }, - new SongSelectTouchInputDetector() }); if (ShowFooter) From 83f8f03c7e3b173766e9c715bd7869929082600b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 Nov 2023 21:46:57 +0900 Subject: [PATCH 183/207] Fix argon health bar not relative sizing correctly --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index f4ce7d1633..4a5faafd8b 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -141,7 +141,13 @@ namespace osu.Game.Screens.Play.HUD Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true); + // we're about to set `RelativeSizeAxes` depending on the value of `UseRelativeSize`. + // setting `RelativeSizeAxes` internally transforms absolute sizing to relative and back to keep the size the same, + // but that is not what we want in this case, since the width at this point is valid in the *target* sizing mode. + // to counteract this, store the numerical value here, and restore it after setting the correct initial relative sizing axes. + float previousWidth = Width; UseRelativeSize.BindValueChanged(v => RelativeSizeAxes = v.NewValue ? Axes.X : Axes.None, true); + Width = previousWidth; BarHeight.BindValueChanged(_ => updatePath(), true); } From a745642f764cf28b37800936186399f459cdd47d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Nov 2023 22:01:56 +0900 Subject: [PATCH 184/207] Fix customised argon skins no longer loading due to incorrect resource store spec --- .../Screens/Play/HUD/ArgonCounterTextComponent.cs | 14 +++++++------- osu.Game/Skinning/ArgonSkin.cs | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 56f60deae1..eabac9a3e6 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -9,11 +9,11 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Framework.Text; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -133,30 +133,30 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(TextureStore textures) { Spacing = new Vector2(-2f, 0f); Font = new FontUsage(@"argon-counter", 1); - glyphStore = new GlyphStore(skin, getLookup); + glyphStore = new GlyphStore(textures, getLookup); } protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); private class GlyphStore : ITexturedGlyphLookupStore { - private readonly ISkin skin; + private readonly TextureStore textures; private readonly Func getLookup; - public GlyphStore(ISkin skin, Func getLookup) + public GlyphStore(TextureStore textures, Func getLookup) { - this.skin = skin; + this.textures = textures; this.getLookup = getLookup; } public ITexturedCharacterGlyph? Get(string fontName, char character) { string lookup = getLookup(character); - var texture = skin.GetTexture($"{fontName}-{lookup}"); + var texture = textures.Get($"Gameplay/Fonts/{fontName}-{lookup}"); if (texture == null) return null; diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 637467c748..226de4088c 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -8,7 +8,6 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.Extensions; @@ -44,8 +43,7 @@ namespace osu.Game.Skinning public ArgonSkin(SkinInfo skin, IStorageResourceProvider resources) : base( skin, - resources, - new NamespacedResourceStore(resources.Resources, "Skins/Argon") + resources ) { Resources = resources; From 9a7d7dda2ae564fef11f3e544be339da42abb8d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Nov 2023 23:04:11 +0900 Subject: [PATCH 185/207] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 81600c1f72..9985afbd8b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 535282ba7d90303058c429c564c9d473afaf8e99 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 14 Nov 2023 14:13:20 -0800 Subject: [PATCH 186/207] Use existing localisation for corner radius in `BoxElement` --- osu.Game/Skinning/Components/BoxElement.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Components/BoxElement.cs b/osu.Game/Skinning/Components/BoxElement.cs index 235f97ceef..f4f913d80a 100644 --- a/osu.Game/Skinning/Components/BoxElement.cs +++ b/osu.Game/Skinning/Components/BoxElement.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Configuration; +using osu.Game.Localisation.SkinComponents; +using osu.Game.Overlays.Settings; using osuTK; using osuTK.Graphics; @@ -16,7 +18,8 @@ namespace osu.Game.Skinning.Components { public bool UsesFixedAnchor { get; set; } - [SettingSource("Corner rounding", "How round the corners of the box should be.")] + [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.CornerRadius), nameof(SkinnableComponentStrings.CornerRadiusDescription), + SettingControlType = typeof(SettingsPercentageSlider))] public BindableFloat CornerRounding { get; } = new BindableFloat(1) { Precision = 0.01f, From 74fb1b5f81475121af75072367759a241aa4db38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Nov 2023 10:40:59 +0900 Subject: [PATCH 187/207] Rename property to match expctations --- osu.Game/Skinning/Components/BoxElement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Components/BoxElement.cs b/osu.Game/Skinning/Components/BoxElement.cs index f4f913d80a..8b556418d2 100644 --- a/osu.Game/Skinning/Components/BoxElement.cs +++ b/osu.Game/Skinning/Components/BoxElement.cs @@ -20,7 +20,7 @@ namespace osu.Game.Skinning.Components [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.CornerRadius), nameof(SkinnableComponentStrings.CornerRadiusDescription), SettingControlType = typeof(SettingsPercentageSlider))] - public BindableFloat CornerRounding { get; } = new BindableFloat(1) + public new BindableFloat CornerRadius { get; } = new BindableFloat(1) { Precision = 0.01f, MinValue = 0, @@ -47,7 +47,7 @@ namespace osu.Game.Skinning.Components { base.Update(); - CornerRadius = CornerRounding.Value * Math.Min(DrawWidth, DrawHeight) * 0.5f; + base.CornerRadius = CornerRadius.Value * Math.Min(DrawWidth, DrawHeight) * 0.5f; } } } From 2264e1e2493b69d0455f57639e60e3e2e10f7e96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Nov 2023 10:45:01 +0900 Subject: [PATCH 188/207] Change default value and range of `BoxElement`'s corner radius to match other usages --- osu.Game/Skinning/ArgonSkin.cs | 5 ++++- osu.Game/Skinning/Components/BoxElement.cs | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 226de4088c..4588c62b0f 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -216,7 +216,10 @@ namespace osu.Game.Skinning }, new ArgonScoreCounter(), new ArgonHealthDisplay(), - new BoxElement(), + new BoxElement + { + CornerRadius = { Value = 0.5f } + }, new ArgonAccuracyCounter(), new ArgonComboCounter { diff --git a/osu.Game/Skinning/Components/BoxElement.cs b/osu.Game/Skinning/Components/BoxElement.cs index 8b556418d2..34d389728c 100644 --- a/osu.Game/Skinning/Components/BoxElement.cs +++ b/osu.Game/Skinning/Components/BoxElement.cs @@ -20,11 +20,11 @@ namespace osu.Game.Skinning.Components [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.CornerRadius), nameof(SkinnableComponentStrings.CornerRadiusDescription), SettingControlType = typeof(SettingsPercentageSlider))] - public new BindableFloat CornerRadius { get; } = new BindableFloat(1) + public new BindableFloat CornerRadius { get; } = new BindableFloat(0.25f) { - Precision = 0.01f, MinValue = 0, - MaxValue = 1, + MaxValue = 0.5f, + Precision = 0.01f }; public BoxElement() @@ -47,7 +47,7 @@ namespace osu.Game.Skinning.Components { base.Update(); - base.CornerRadius = CornerRadius.Value * Math.Min(DrawWidth, DrawHeight) * 0.5f; + base.CornerRadius = CornerRadius.Value * Math.Min(DrawWidth, DrawHeight); } } } From 159cf41f824546013c22ec454fe1bb33a460f2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 15 Nov 2023 12:24:47 +0900 Subject: [PATCH 189/207] Fix default argon health bar width being zero Closes #25460. --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 4a5faafd8b..82203d7891 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -94,6 +94,13 @@ namespace osu.Game.Screens.Play.HUD public ArgonHealthDisplay() { AddLayout(drawSizeLayout); + + // sane default width specification. + // this only matters if the health display isn't part of the default skin + // (in which case width will be set to 300 via `ArgonSkin.GetDrawableComponent()`), + // and if the user hasn't applied their own modifications + // (which are applied via `SerialisedDrawableInfo.ApplySerialisedInfo()`). + Width = 0.98f; } [BackgroundDependencyLoader] From 71714f8f6e6bd7d2caf9214ff267c05e550352e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 15 Nov 2023 13:33:03 +0900 Subject: [PATCH 190/207] Add back test step for testing out argon health bar width --- .../Visual/Gameplay/TestSceneArgonHealthDisplay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index f51577bc84..6c364e41c7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -52,6 +52,12 @@ namespace osu.Game.Tests.Visual.Gameplay if (healthDisplay.IsNotNull()) healthDisplay.BarHeight.Value = val; }); + + AddSliderStep("Width", 0, 1f, 0.98f, val => + { + if (healthDisplay.IsNotNull()) + healthDisplay.Width = val; + }); } [Test] From 87ace7565dfffebbcfb34f48743406a0c68d1065 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 14 Nov 2023 20:44:33 -0800 Subject: [PATCH 191/207] Use existing localisation for argon counter component labels --- osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs | 2 +- osu.Game/Screens/Play/HUD/ArgonComboCounter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 5f9441a5c4..55fdf24e8f 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Play.HUD new Container { AutoSizeAxes = Axes.Both, - Child = wholePart = new ArgonCounterTextComponent(Anchor.TopRight, "ACCURACY") + Child = wholePart = new ArgonCounterTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersAccuracy.ToUpper()) { RequiredDisplayDigits = { Value = 3 }, WireframeOpacity = { BindTarget = WireframeOpacity } diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index ac710294ef..52af9b0247 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Play.HUD protected override LocalisableString FormatCount(int count) => $@"{count}x"; - protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopLeft, "COMBO") + protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopLeft, MatchesStrings.MatchScoreStatsCombo.ToUpper()) { WireframeOpacity = { BindTarget = WireframeOpacity }, }; From 62352ce5f3b6ce56c310a83dccd11d9eb15f90fa Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 14 Nov 2023 20:45:23 -0800 Subject: [PATCH 192/207] Add ability to toggle labels on argon counter components --- .../SkinnableComponentStrings.cs | 10 +++++++ .../Screens/Play/HUD/ArgonAccuracyCounter.cs | 28 ++++++++++++++++--- .../Screens/Play/HUD/ArgonComboCounter.cs | 7 +++++ .../Play/HUD/ArgonCounterTextComponent.cs | 4 ++- .../Screens/Play/HUD/ArgonScoreCounter.cs | 8 +++++- 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs index 7c11ea6ac6..639f5c9b16 100644 --- a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs +++ b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs @@ -49,6 +49,16 @@ namespace osu.Game.Localisation.SkinComponents /// public static LocalisableString CornerRadiusDescription => new TranslatableString(getKey(@"corner_radius_description"), "How rounded the corners should be."); + /// + /// "Show label" + /// + public static LocalisableString ShowLabel => new TranslatableString(getKey(@"show_label"), @"Show label"); + + /// + /// "Whether the label should be shown." + /// + public static LocalisableString ShowLabelDescription => new TranslatableString(getKey(@"show_label_description"), @"Whether the label should be shown."); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 55fdf24e8f..521ad63426 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation.SkinComponents; +using osu.Game.Resources.Localisation.Web; using osu.Game.Skinning; using osuTK; @@ -25,20 +28,27 @@ namespace osu.Game.Screens.Play.HUD MaxValue = 1, }; + [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel), nameof(SkinnableComponentStrings.ShowLabelDescription))] + public Bindable ShowLabel { get; } = new BindableBool(true); + public bool UsesFixedAnchor { get; set; } protected override IHasText CreateText() => new ArgonAccuracyTextComponent { WireframeOpacity = { BindTarget = WireframeOpacity }, + ShowLabel = { BindTarget = ShowLabel }, }; private partial class ArgonAccuracyTextComponent : CompositeDrawable, IHasText { private readonly ArgonCounterTextComponent wholePart; private readonly ArgonCounterTextComponent fractionPart; + private readonly ArgonCounterTextComponent percentText; public IBindable WireframeOpacity { get; } = new BindableFloat(); + public Bindable ShowLabel { get; } = new BindableBool(); + public LocalisableString Text { get => wholePart.Text; @@ -67,24 +77,34 @@ namespace osu.Game.Screens.Play.HUD Child = wholePart = new ArgonCounterTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersAccuracy.ToUpper()) { RequiredDisplayDigits = { Value = 3 }, - WireframeOpacity = { BindTarget = WireframeOpacity } + WireframeOpacity = { BindTarget = WireframeOpacity }, + ShowLabel = { BindTarget = ShowLabel }, } }, fractionPart = new ArgonCounterTextComponent(Anchor.TopLeft) { - Margin = new MarginPadding { Top = 12f * 2f + 4f }, // +4 to account for the extra spaces above the digits. WireframeOpacity = { BindTarget = WireframeOpacity }, Scale = new Vector2(0.5f), }, - new ArgonCounterTextComponent(Anchor.TopLeft) + percentText = new ArgonCounterTextComponent(Anchor.TopLeft) { Text = @"%", - Margin = new MarginPadding { Top = 12f }, WireframeOpacity = { BindTarget = WireframeOpacity } }, } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowLabel.BindValueChanged(s => + { + fractionPart.Margin = new MarginPadding { Top = s.NewValue ? 12f * 2f + 4f : 4f }; // +4 to account for the extra spaces above the digits. + percentText.Margin = new MarginPadding { Top = s.NewValue ? 12f : 0 }; + }, true); + } } } } diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index 52af9b0247..5ea7fd0b82 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -4,10 +4,13 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation.SkinComponents; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -29,6 +32,9 @@ namespace osu.Game.Screens.Play.HUD MaxValue = 1, }; + [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel), nameof(SkinnableComponentStrings.ShowLabelDescription))] + public Bindable ShowLabel { get; } = new BindableBool(true); + [BackgroundDependencyLoader] private void load(ScoreProcessor scoreProcessor) { @@ -56,6 +62,7 @@ namespace osu.Game.Screens.Play.HUD protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopLeft, MatchesStrings.MatchScoreStatsCombo.ToUpper()) { WireframeOpacity = { BindTarget = WireframeOpacity }, + ShowLabel = { BindTarget = ShowLabel }, }; } } diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index eabac9a3e6..d3fadb452b 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -26,6 +26,7 @@ namespace osu.Game.Screens.Play.HUD public IBindable WireframeOpacity { get; } = new BindableFloat(); public Bindable RequiredDisplayDigits { get; } = new BindableInt(); + public Bindable ShowLabel { get; } = new BindableBool(); public Container NumberContainer { get; private set; } @@ -56,7 +57,7 @@ namespace osu.Game.Screens.Play.HUD { labelText = new OsuSpriteText { - Alpha = label != null ? 1 : 0, + Alpha = 0, Text = label.GetValueOrDefault(), Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), Margin = new MarginPadding { Left = 2.5f }, @@ -114,6 +115,7 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); WireframeOpacity.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true); + ShowLabel.BindValueChanged(s => labelText.Alpha = s.NewValue ? 1 : 0, true); } private partial class ArgonCounterSpriteText : OsuSpriteText diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index 0192fa3c02..d661cd67cc 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation.SkinComponents; +using osu.Game.Resources.Localisation.Web; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD @@ -24,14 +26,18 @@ namespace osu.Game.Screens.Play.HUD MaxValue = 1, }; + [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel), nameof(SkinnableComponentStrings.ShowLabelDescription))] + public Bindable ShowLabel { get; } = new BindableBool(); + public bool UsesFixedAnchor { get; set; } protected override LocalisableString FormatCount(long count) => count.ToLocalisableString(); - protected override IHasText CreateText() => new ArgonScoreTextComponent(Anchor.TopRight) + protected override IHasText CreateText() => new ArgonScoreTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersScore.ToUpper()) { RequiredDisplayDigits = { BindTarget = RequiredDisplayDigits }, WireframeOpacity = { BindTarget = WireframeOpacity }, + ShowLabel = { BindTarget = ShowLabel }, }; private partial class ArgonScoreTextComponent : ArgonCounterTextComponent From a5e1dd8107a8770ab8698cc39a17df2e81847a8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Nov 2023 14:07:51 +0900 Subject: [PATCH 193/207] Add test coverage of deserialising a modified Argon skin --- osu.Game.Tests/Skins/SkinDeserialisationTest.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs index f68250e0fa..c45eadeff2 100644 --- a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs +++ b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs @@ -15,6 +15,7 @@ using osu.Game.IO.Archives; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Skinning; +using osu.Game.Skinning.Components; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Skins @@ -102,6 +103,20 @@ namespace osu.Game.Tests.Skins } } + [Test] + public void TestDeserialiseModifiedArgon() + { + using (var stream = TestResources.OpenResource("Archives/modified-argon-20231106.osk")) + using (var storage = new ZipArchiveReader(stream)) + { + var skin = new TestSkin(new SkinInfo(), null, storage); + + Assert.That(skin.LayoutInfos, Has.Count.EqualTo(2)); + Assert.That(skin.LayoutInfos[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(10)); + Assert.That(skin.LayoutInfos[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(PlayerName))); + } + } + [Test] public void TestDeserialiseModifiedClassic() { From aac1854d832ff8ad48333ed0afd968ab50dcf712 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Nov 2023 14:08:05 +0900 Subject: [PATCH 194/207] Add test coverage of layout retrievable after importing modified skins --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index ab3e099c3a..98f7dc5444 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -9,10 +9,12 @@ using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Skinning; +using osu.Game.Tests.Resources; using SharpCompress.Archives.Zip; namespace osu.Game.Tests.Skins.IO @@ -21,6 +23,25 @@ namespace osu.Game.Tests.Skins.IO { #region Testing filename metadata inclusion + [TestCase("Archives/modified-classic-20220723.osk")] + [TestCase("Archives/modified-default-20230117.osk")] + [TestCase("Archives/modified-argon-20231106.osk")] + public Task TestImportModifiedSkinHasResources(string archive) => runSkinTest(async osu => + { + using (var stream = TestResources.OpenResource(archive)) + { + var imported = await loadSkinIntoOsu(osu, new ImportTask(stream, "skin.osk")); + + // When the import filename doesn't match, it should be appended (and update the skin.ini). + + var skinManager = osu.Dependencies.Get(); + + skinManager.CurrentSkinInfo.Value = imported; + + Assert.That(skinManager.CurrentSkin.Value.LayoutInfos.Count, Is.EqualTo(2)); + } + }); + [Test] public Task TestSingleImportDifferentFilename() => runSkinTest(async osu => { From 8cd1f08a923b1b38e7b096e59211c0714089d1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 15 Nov 2023 13:33:12 +0900 Subject: [PATCH 195/207] Fix argon health bar folding in on itself --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 82203d7891..372d2bab85 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -89,6 +89,11 @@ namespace osu.Game.Screens.Play.HUD public const float MAIN_PATH_RADIUS = 10f; + private const float curve_start_offset = 70; + private const float curve_end_offset = 40; + private const float padding = MAIN_PATH_RADIUS * 2; + private const float curve_smoothness = 10; + private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); public ArgonHealthDisplay() @@ -248,11 +253,17 @@ namespace osu.Game.Screens.Play.HUD private void updatePath() { - float barLength = DrawWidth - MAIN_PATH_RADIUS * 2; - float curveStart = barLength - 70; - float curveEnd = barLength - 40; + float usableWidth = DrawWidth - padding; - const float curve_smoothness = 10; + if (usableWidth < 0) enforceMinimumWidth(); + + // the display starts curving at `curve_start_offset` units from the right and ends curving at `curve_end_offset`. + // to ensure that the curve is symmetric when it starts being narrow enough, add a `curve_end_offset` to the left side too. + const float rescale_cutoff = curve_start_offset + curve_end_offset; + + float barLength = Math.Max(DrawWidth - padding, rescale_cutoff); + float curveStart = barLength - curve_start_offset; + float curveEnd = barLength - curve_end_offset; Vector2 diagonalDir = (new Vector2(curveEnd, BarHeight.Value) - new Vector2(curveStart, 0)).Normalized(); @@ -268,6 +279,9 @@ namespace osu.Game.Screens.Play.HUD new PathControlPoint(new Vector2(barLength, BarHeight.Value)), }); + if (DrawWidth - padding < rescale_cutoff) + rescalePathProportionally(); + List vertices = new List(); barPath.GetPathToProgress(vertices, 0.0, 1.0); @@ -276,6 +290,20 @@ namespace osu.Game.Screens.Play.HUD glowBar.Vertices = vertices; updatePathVertices(); + + void enforceMinimumWidth() + { + var relativeAxes = RelativeSizeAxes; + RelativeSizeAxes = Axes.None; + Width = padding; + RelativeSizeAxes = relativeAxes; + } + + void rescalePathProportionally() + { + foreach (var point in barPath.ControlPoints) + point.Position = new Vector2(point.Position.X / barLength * (DrawWidth - padding), point.Position.Y); + } } private void updatePathVertices() From aa9deecafe7ad9150d82bfa7015d7e21cf3888b5 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Fri, 10 Nov 2023 11:37:23 +0100 Subject: [PATCH 196/207] added missing comment, fixed incorrect visibility --- osu.Game/Users/Drawables/ClickableAvatar.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index ef451df95d..1f1960714f 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -33,7 +33,7 @@ namespace osu.Game.Users.Drawables /// A clickable avatar for the specified user, with UI sounds included. /// /// The user. A null value will get a placeholder avatar. - /// + /// If set to true, the will be shown for the tooltip public ClickableAvatar(APIUser? user = null, bool showCardOnHover = false) { if (user?.Id != APIUser.SYSTEM_USER_ID) @@ -72,7 +72,11 @@ namespace osu.Game.Users.Drawables } protected override void PopIn() => this.FadeIn(150, Easing.OutQuint); - protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint); + protected override void PopOut() + { + this.Delay(150).FadeOut(500, Easing.OutQuint); + Clear(); + } public void Move(Vector2 pos) => Position = pos; From deef8998f73850d0e40247fa5854f042967e4687 Mon Sep 17 00:00:00 2001 From: Joshua Hegedus Date: Fri, 10 Nov 2023 11:45:31 +0100 Subject: [PATCH 197/207] reverted the change --- osu.Game/Users/Drawables/ClickableAvatar.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index 1f1960714f..26622a1f30 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -72,11 +72,7 @@ namespace osu.Game.Users.Drawables } protected override void PopIn() => this.FadeIn(150, Easing.OutQuint); - protected override void PopOut() - { - this.Delay(150).FadeOut(500, Easing.OutQuint); - Clear(); - } + protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint); public void Move(Vector2 pos) => Position = pos; From 7b987266d50db8b3c5668a27625f7635a6eeefbc Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 14 Nov 2023 16:15:22 -0800 Subject: [PATCH 198/207] Change behavior of some clickable avatars in line with web --- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 2 +- osu.Game/Overlays/Comments/CommentsContainer.cs | 2 +- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index 1d01495188..99ad5a5c7d 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet AutoSizeAxes = Axes.Both, CornerRadius = 4, Masking = true, - Child = avatar = new UpdateableAvatar(showGuestOnNull: false) + Child = avatar = new UpdateableAvatar(showUserPanelOnHover: true, showGuestOnNull: false) { Size = new Vector2(height), }, diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index af5f4dd280..b4e9a80ff1 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -102,7 +102,7 @@ namespace osu.Game.Overlays.Comments Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 20 }, Children = new Drawable[] { - avatar = new UpdateableAvatar(api.LocalUser.Value) + avatar = new UpdateableAvatar(api.LocalUser.Value, isInteractive: false) { Size = new Vector2(50), CornerExponent = 2, diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index ba1c7ca8b2..ceae17aa5d 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -144,7 +144,7 @@ namespace osu.Game.Overlays.Comments Size = new Vector2(avatar_size), Children = new Drawable[] { - new UpdateableAvatar(Comment.User) + new UpdateableAvatar(Comment.User, showUserPanelOnHover: true) { Size = new Vector2(avatar_size), Masking = true, From b118999120ec13d4632f032429fbc3a26cc43db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 15 Nov 2023 18:27:08 +0900 Subject: [PATCH 199/207] Remove unused using directive --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 98f7dc5444..606a5afac2 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Platform; -using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Extensions; using osu.Game.IO; From 2987c0e802ec597ab7480c9d3623b423592669bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Nov 2023 19:01:52 +0900 Subject: [PATCH 200/207] Add note about enfocing size methodology --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 372d2bab85..5e6130d3f8 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -293,9 +293,13 @@ namespace osu.Game.Screens.Play.HUD void enforceMinimumWidth() { - var relativeAxes = RelativeSizeAxes; + // Switch to absolute in order to be able to define a minimum width. + // Then switch back is required. Framework will handle the conversion for us. + Axes relativeAxes = RelativeSizeAxes; RelativeSizeAxes = Axes.None; + Width = padding; + RelativeSizeAxes = relativeAxes; } From 2b19cf6ce4b908880db4854a4f74d30edd7714ac Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 15 Nov 2023 19:43:25 -0800 Subject: [PATCH 201/207] Fix comment markdown style regression --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 3 +-- .../Markdown/Footnotes/OsuMarkdownFootnoteTooltip.cs | 3 +-- osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs | 2 +- osu.Game/Overlays/Comments/CommentMarkdownContainer.cs | 4 ++-- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 3 +-- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 2 +- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 0aa0295f7d..d4b6bc2b91 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -10,7 +10,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; @@ -298,7 +297,7 @@ This is a line after the fenced code block! { public LinkInline Link; - public override MarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer + public override OsuMarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer { UrlAdded = link => Link = link, }; diff --git a/osu.Game/Graphics/Containers/Markdown/Footnotes/OsuMarkdownFootnoteTooltip.cs b/osu.Game/Graphics/Containers/Markdown/Footnotes/OsuMarkdownFootnoteTooltip.cs index af64913212..b9725de5f4 100644 --- a/osu.Game/Graphics/Containers/Markdown/Footnotes/OsuMarkdownFootnoteTooltip.cs +++ b/osu.Game/Graphics/Containers/Markdown/Footnotes/OsuMarkdownFootnoteTooltip.cs @@ -5,7 +5,6 @@ using Markdig.Extensions.Footnotes; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Overlays; @@ -62,7 +61,7 @@ namespace osu.Game.Graphics.Containers.Markdown.Footnotes lastFootnote = Text = footnote; } - public override MarkdownTextFlowContainer CreateTextFlow() => new FootnoteMarkdownTextFlowContainer(); + public override OsuMarkdownTextFlowContainer CreateTextFlow() => new FootnoteMarkdownTextFlowContainer(); } private partial class FootnoteMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index 5da785603a..b4031752db 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Containers.Markdown Font = OsuFont.GetFont(Typeface.Inter, size: 14, weight: FontWeight.Regular), }; - public override MarkdownTextFlowContainer CreateTextFlow() => new OsuMarkdownTextFlowContainer(); + public override OsuMarkdownTextFlowContainer CreateTextFlow() => new OsuMarkdownTextFlowContainer(); protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new OsuMarkdownHeading(headingBlock); diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index e48a52c787..13446792aa 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Comments protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock); - public override MarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer(); + public override OsuMarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer(); private partial class CommentMarkdownHeading : OsuMarkdownHeading { @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Comments } } - private partial class CommentMarkdownTextFlowContainer : MarkdownTextFlowContainer + private partial class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { protected override void AddImage(LinkInline linkInline) => AddDrawable(new CommentMarkdownImage(linkInline.Url)); diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 9107ad342b..e6bfb75026 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -7,7 +7,6 @@ using Markdig.Extensions.Yaml; using Markdig.Syntax; using Markdig.Syntax.Inlines; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; namespace osu.Game.Overlays.Wiki.Markdown @@ -53,7 +52,7 @@ namespace osu.Game.Overlays.Wiki.Markdown base.AddMarkdownComponent(markdownObject, container, level); } - public override MarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer(); + public override OsuMarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer(); private partial class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index c5b71cfeb6..cbffe5732e 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Wiki public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With(Typeface.Torus, weight: FontWeight.Bold)); - public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); + public override OsuMarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level) => base.CreateParagraph(paragraphBlock, level).With(p => p.Margin = new MarginPadding { Bottom = 10 }); From dc2d5749651de89bd1e4d5bba615068bfce1ac30 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 15 Nov 2023 19:41:13 -0800 Subject: [PATCH 202/207] Fix comment markdown image not showing tooltips --- osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs | 2 +- osu.Game/Overlays/Comments/CommentMarkdownContainer.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs index 97e1cae11c..5e83dd4fb3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online new[] { "Plain", "This is plain comment" }, new[] { "Pinned", "This is pinned comment" }, new[] { "Link", "Please visit https://osu.ppy.sh" }, - new[] { "Big Image", "![](Backgrounds/bg1)" }, + new[] { "Big Image", "![](Backgrounds/bg1 \"Big Image\")" }, new[] { "Small Image", "![](Cursor/cursortrail)" }, new[] { diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index 13446792aa..51e1b863c7 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -51,12 +51,12 @@ namespace osu.Game.Overlays.Comments private partial class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { - protected override void AddImage(LinkInline linkInline) => AddDrawable(new CommentMarkdownImage(linkInline.Url)); + protected override void AddImage(LinkInline linkInline) => AddDrawable(new CommentMarkdownImage(linkInline)); - private partial class CommentMarkdownImage : MarkdownImage + private partial class CommentMarkdownImage : OsuMarkdownImage { - public CommentMarkdownImage(string url) - : base(url) + public CommentMarkdownImage(LinkInline linkInline) + : base(linkInline) { } From 39a3313929035bc50e308a5ecee8f568f85dbe76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Nov 2023 14:11:01 +0900 Subject: [PATCH 203/207] Update tooltip description slightly --- .../SkinnableComponentStrings.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs index 639f5c9b16..d5c8d5ccec 100644 --- a/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs +++ b/osu.Game/Localisation/SkinComponents/SkinnableComponentStrings.cs @@ -12,42 +12,42 @@ namespace osu.Game.Localisation.SkinComponents /// /// "Sprite name" /// - public static LocalisableString SpriteName => new TranslatableString(getKey(@"sprite_name"), "Sprite name"); + public static LocalisableString SpriteName => new TranslatableString(getKey(@"sprite_name"), @"Sprite name"); /// /// "The filename of the sprite" /// - public static LocalisableString SpriteNameDescription => new TranslatableString(getKey(@"sprite_name_description"), "The filename of the sprite"); + public static LocalisableString SpriteNameDescription => new TranslatableString(getKey(@"sprite_name_description"), @"The filename of the sprite"); /// /// "Font" /// - public static LocalisableString Font => new TranslatableString(getKey(@"font"), "Font"); + public static LocalisableString Font => new TranslatableString(getKey(@"font"), @"Font"); /// /// "The font to use." /// - public static LocalisableString FontDescription => new TranslatableString(getKey(@"font_description"), "The font to use."); + public static LocalisableString FontDescription => new TranslatableString(getKey(@"font_description"), @"The font to use."); /// /// "Text" /// - public static LocalisableString TextElementText => new TranslatableString(getKey(@"text_element_text"), "Text"); + public static LocalisableString TextElementText => new TranslatableString(getKey(@"text_element_text"), @"Text"); /// /// "The text to be displayed." /// - public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), "The text to be displayed."); + public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), @"The text to be displayed."); /// /// "Corner radius" /// - public static LocalisableString CornerRadius => new TranslatableString(getKey(@"corner_radius"), "Corner radius"); + public static LocalisableString CornerRadius => new TranslatableString(getKey(@"corner_radius"), @"Corner radius"); /// /// "How rounded the corners should be." /// - public static LocalisableString CornerRadiusDescription => new TranslatableString(getKey(@"corner_radius_description"), "How rounded the corners should be."); + public static LocalisableString CornerRadiusDescription => new TranslatableString(getKey(@"corner_radius_description"), @"How rounded the corners should be."); /// /// "Show label" @@ -55,10 +55,10 @@ namespace osu.Game.Localisation.SkinComponents public static LocalisableString ShowLabel => new TranslatableString(getKey(@"show_label"), @"Show label"); /// - /// "Whether the label should be shown." + /// "Whether the component's label should be shown." /// - public static LocalisableString ShowLabelDescription => new TranslatableString(getKey(@"show_label_description"), @"Whether the label should be shown."); + public static LocalisableString ShowLabelDescription => new TranslatableString(getKey(@"show_label_description"), @"Whether the component's label should be shown."); - private static string getKey(string key) => $"{prefix}:{key}"; + private static string getKey(string key) => $@"{prefix}:{key}"; } } From 73eda6c09c259e0f517ffa75a0affb8902ef1fd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Nov 2023 14:18:49 +0900 Subject: [PATCH 204/207] Move non-matching default value to argon skin default speficiation instead --- osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs | 2 +- osu.Game/Skinning/ArgonSkin.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index d661cd67cc..005f7e36a7 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD }; [SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel), nameof(SkinnableComponentStrings.ShowLabelDescription))] - public Bindable ShowLabel { get; } = new BindableBool(); + public Bindable ShowLabel { get; } = new BindableBool(true); public bool UsesFixedAnchor { get; set; } diff --git a/osu.Game/Skinning/ArgonSkin.cs b/osu.Game/Skinning/ArgonSkin.cs index 4588c62b0f..6fcab6a977 100644 --- a/osu.Game/Skinning/ArgonSkin.cs +++ b/osu.Game/Skinning/ArgonSkin.cs @@ -214,7 +214,10 @@ namespace osu.Game.Skinning Size = new Vector2(380, 72), Position = new Vector2(4, 5) }, - new ArgonScoreCounter(), + new ArgonScoreCounter + { + ShowLabel = { Value = false }, + }, new ArgonHealthDisplay(), new BoxElement { From dbd4f26436c988a8d1e1afb6e893e82fedb25460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 16 Nov 2023 15:37:53 +0900 Subject: [PATCH 205/207] Use alternative method of scheduling storyboard unload --- osu.Game/Screens/BackgroundScreenStack.cs | 18 ------------------ .../Backgrounds/BackgroundScreenDefault.cs | 6 +++++- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 562b212561..99ca383b9f 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Screens; -using osu.Framework.Threading; -using osu.Game.Screens.Backgrounds; namespace osu.Game.Screens { @@ -36,20 +33,5 @@ namespace osu.Game.Screens base.Push(screen); return true; } - - /// - /// Schedules a delegate to run after 500ms, the time length of a background screen transition. - /// This is used in to dispose of the storyboard once the background screen is completely off-screen. - /// - /// - /// Late storyboard disposals cannot be achieved with any local scheduler from or any component inside it, - /// due to the screen becoming dead at the moment the transition finishes. And, on the frame that it is dead on, it will not receive an , - /// therefore not guaranteeing to dispose the storyboard at any period of time close to the end of the transition. - /// This might require reconsideration framework-side, possibly exposing a "death" event in or all s in general. - /// - /// The delegate - /// - /// - internal ScheduledDelegate ScheduleUntilTransitionEnd(Action action) => Scheduler.AddDelayed(action, BackgroundScreen.TRANSITION_LENGTH); } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 4583b3e4d6..e46b92795a 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Framework.Utils; @@ -36,6 +37,9 @@ namespace osu.Game.Screens.Backgrounds [Resolved] private IBindable beatmap { get; set; } + [Resolved] + private GameHost gameHost { get; set; } + protected virtual bool AllowStoryboardBackground => true; public BackgroundScreenDefault(bool animateOnEnter = true) @@ -81,7 +85,7 @@ namespace osu.Game.Screens.Backgrounds Debug.Assert(backgroundScreenStack != null); if (background is BeatmapBackgroundWithStoryboard storyboardBackground) - storyboardUnloadDelegate = backgroundScreenStack.ScheduleUntilTransitionEnd(storyboardBackground.UnloadStoryboard); + storyboardUnloadDelegate = gameHost.UpdateThread.Scheduler.AddDelayed(storyboardBackground.UnloadStoryboard, TRANSITION_LENGTH); base.OnSuspending(e); } From b88e3cd26f8eb3f7b90e8bc6a96097ae23d3773e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Nov 2023 20:16:23 +0900 Subject: [PATCH 206/207] Change `ResourceStore` provided to `Skin` to be a fallback, not replacement --- .../CatchSkinColourDecodingTest.cs | 4 ++-- .../Formats/LegacyBeatmapEncoderTest.cs | 4 ++-- .../Skins/SkinDeserialisationTest.cs | 4 ++-- .../Skins/TestSceneSkinResources.cs | 4 ++-- osu.Game/Skinning/DefaultLegacySkin.cs | 3 +-- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 7 +++---- osu.Game/Skinning/Skin.cs | 21 +++++++++++-------- osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 ++-- 9 files changed, 27 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs index 72011042bc..74b02bab9b 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs @@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestLegacySkin : LegacySkin { - public TestLegacySkin(SkinInfo skin, IResourceStore storage) + public TestLegacySkin(SkinInfo skin, IResourceStore fallbackStore) // Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null). - : base(skin, null, storage) + : base(skin, null, fallbackStore) { } } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 5d9049ead7..9ff0fe874f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -174,8 +174,8 @@ namespace osu.Game.Tests.Beatmaps.Formats private class TestLegacySkin : LegacySkin { - public TestLegacySkin(IResourceStore storage, string fileName) - : base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, storage, fileName) + public TestLegacySkin(IResourceStore fallbackStore, string fileName) + : base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, fallbackStore, fileName) { } } diff --git a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs index c45eadeff2..6423e061c5 100644 --- a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs +++ b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs @@ -149,8 +149,8 @@ namespace osu.Game.Tests.Skins private class TestSkin : Skin { - public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage = null, string configurationFilename = "skin.ini") - : base(skin, resources, storage, configurationFilename) + public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore = null, string configurationFilename = "skin.ini") + : base(skin, resources, fallbackStore, configurationFilename) { } diff --git a/osu.Game.Tests/Skins/TestSceneSkinResources.cs b/osu.Game.Tests/Skins/TestSceneSkinResources.cs index aaec319b57..e77affd817 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinResources.cs @@ -95,8 +95,8 @@ namespace osu.Game.Tests.Skins { public const string SAMPLE_NAME = "test-sample"; - public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage = null, string configurationFilename = "skin.ini") - : base(skin, resources, storage, configurationFilename) + public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore = null, string configurationFilename = "skin.ini") + : base(skin, resources, fallbackStore, configurationFilename) { } diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index fd9653e3e5..34ea0af122 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -31,8 +31,7 @@ namespace osu.Game.Skinning : base( skin, resources, - // In the case of the actual default legacy skin (ie. the fallback one, which a user hasn't applied any modifications to) we want to use the game provided resources. - skin.Protected ? new NamespacedResourceStore(resources.Resources, "Skins/Legacy") : null + new NamespacedResourceStore(resources.Resources, "Skins/Legacy") ) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 90eb5fa013..d6ba6ea332 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -73,7 +73,7 @@ namespace osu.Game.Skinning // needs to be removed else it will cause incorrect skin behaviours. This is due to the config lookup having no context of which skin // it should be returning the version for. - Skin.LogLookupDebug(this, lookup, Skin.LookupDebugType.Miss); + LogLookupDebug(this, lookup, LookupDebugType.Miss); return null; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index dc683f1dae..2e91770919 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -16,7 +16,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; -using osu.Game.Database; using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Rulesets.Objects.Types; @@ -51,10 +50,10 @@ namespace osu.Game.Skinning /// /// The model for this skin. /// Access to raw game resources. - /// An optional store which will be used for looking up skin resources. If null, one will be created from realm pattern. + /// An optional fallback store which will be used for file lookups that are not serviced by realm user storage. /// The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file. - protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage, string configurationFilename = @"skin.ini") - : base(skin, resources, storage, configurationFilename) + protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore, string configurationFilename = @"skin.ini") + : base(skin, resources, fallbackStore, configurationFilename) { } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 1e312142d7..9ee69d033d 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -55,7 +55,7 @@ namespace osu.Game.Skinning where TLookup : notnull where TValue : notnull; - private readonly RealmBackedResourceStore? realmBackedStorage; + private readonly ResourceStore store = new ResourceStore(); public string Name { get; } @@ -64,9 +64,9 @@ namespace osu.Game.Skinning /// /// The skin's metadata. Usually a live realm object. /// Access to game-wide resources. - /// An optional store which will *replace* all file lookups that are usually sourced from . + /// An optional fallback store which will be used for file lookups that are not serviced by realm user storage. /// An optional filename to read the skin configuration from. If not provided, the configuration will be retrieved from the storage using "skin.ini". - protected Skin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage = null, string configurationFilename = @"skin.ini") + protected Skin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore = null, string configurationFilename = @"skin.ini") { Name = skin.Name; @@ -74,9 +74,9 @@ namespace osu.Game.Skinning { SkinInfo = skin.ToLive(resources.RealmAccess); - storage ??= realmBackedStorage = new RealmBackedResourceStore(SkinInfo, resources.Files, resources.RealmAccess); + store.AddStore(new RealmBackedResourceStore(SkinInfo, resources.Files, resources.RealmAccess)); - var samples = resources.AudioManager?.GetSampleStore(storage); + var samples = resources.AudioManager?.GetSampleStore(store); if (samples != null) { @@ -88,7 +88,7 @@ namespace osu.Game.Skinning } Samples = samples; - Textures = new TextureStore(resources.Renderer, CreateTextureLoaderStore(resources, storage)); + Textures = new TextureStore(resources.Renderer, CreateTextureLoaderStore(resources, store)); } else { @@ -96,7 +96,10 @@ namespace osu.Game.Skinning SkinInfo = skin.ToLiveUnmanaged(); } - var configurationStream = storage?.GetStream(configurationFilename); + if (fallbackStore != null) + store.AddStore(fallbackStore); + + var configurationStream = store.GetStream(configurationFilename); if (configurationStream != null) { @@ -119,7 +122,7 @@ namespace osu.Game.Skinning { string filename = $"{skinnableTarget}.json"; - byte[]? bytes = storage?.Get(filename); + byte[]? bytes = store?.Get(filename); if (bytes == null) continue; @@ -252,7 +255,7 @@ namespace osu.Game.Skinning Textures?.Dispose(); Samples?.Dispose(); - realmBackedStorage?.Dispose(); + store.Dispose(); } #endregion diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index aab1b72990..f371cf721f 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -201,8 +201,8 @@ namespace osu.Game.Tests.Visual { private readonly bool extrapolateAnimations; - public TestLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources, bool extrapolateAnimations) - : base(skin, resources, storage) + public TestLegacySkin(SkinInfo skin, IResourceStore fallbackStore, IStorageResourceProvider resources, bool extrapolateAnimations) + : base(skin, resources, fallbackStore) { this.extrapolateAnimations = extrapolateAnimations; } From bd932a5417dd7bc4fba8b237832d556c27ea0d9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 17:07:21 +0900 Subject: [PATCH 207/207] Update localisation analyser Pulls in https://github.com/ppy/osu-localisation-analyser/pull/60. --- .config/dotnet-tools.json | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 3cecb0d07c..b8dc201559 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -21,10 +21,10 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2023.712.0", + "version": "2023.1117.0", "commands": [ "localisation" ] } } -} \ No newline at end of file +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9985afbd8b..10ca49c768 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive