diff --git a/osu.Android.props b/osu.Android.props index 8f83c9730b..c0be002bb5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,8 +51,8 @@ - + diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index d9ad95f96a..3ee1b3da30 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -137,12 +137,13 @@ namespace osu.Desktop { base.SetHost(host); - var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"); - var desktopWindow = (SDL2DesktopWindow)host.Window; + var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"); + if (iconStream != null) + desktopWindow.SetIconFromStream(iconStream); + desktopWindow.CursorState |= CursorState.Hidden; - desktopWindow.SetIconFromStream(iconStream); desktopWindow.Title = Name; desktopWindow.DragDrop += f => fileDrop(new[] { f }); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 6f4ca30bd0..d9d0d28477 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -11,7 +11,9 @@ using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Scoring; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -64,6 +66,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ScaleBindable.UnbindFrom(HitObject.ScaleBindable); } + protected override void UpdateInitialTransforms() + { + base.UpdateInitialTransforms(); + + // Dim should only be applied at a top level, as it will be implicitly applied to nested objects. + if (ParentHitObject == null) + { + // Of note, no one noticed this was missing for years, but it definitely feels like it should still exist. + // For now this is applied across all skins, and matches stable. + // For simplicity, dim colour is applied to the DrawableHitObject itself. + // We may need to make a nested container setup if this even causes a usage conflict (ie. with a mod). + this.FadeColour(new Color4(195, 195, 195, 255)); + using (BeginDelayedSequence(InitialLifetimeOffset - OsuHitWindows.MISS_WINDOW)) + this.FadeColour(Color4.White, 100); + } + } + protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; private OsuInputManager osuActionInputManager; diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs index 05fbac625e..6f55e1790f 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -1,20 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { public class OsuHitWindows : HitWindows { + /// + /// osu! ruleset has a fixed miss window regardless of difficulty settings. + /// + public const double MISS_WINDOW = 400; + private static readonly DifficultyRange[] osu_ranges = { new DifficultyRange(HitResult.Great, 80, 50, 20), new DifficultyRange(HitResult.Ok, 140, 100, 60), new DifficultyRange(HitResult.Meh, 200, 150, 100), - new DifficultyRange(HitResult.Miss, 400, 400, 400), + new DifficultyRange(HitResult.Miss, MISS_WINDOW, MISS_WINDOW, MISS_WINDOW), }; public override bool IsHitResultAllowed(HitResult result) diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 3f20f843a7..e7590df3e0 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; -using System.Threading.Tasks; using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; @@ -78,7 +77,7 @@ namespace osu.Game.Tests.Online } }; - beatmaps.AllowImport = new TaskCompletionSource(); + beatmaps.AllowImport.Reset(); testBeatmapFile = TestResources.GetQuickTestBeatmapForImport(); @@ -132,7 +131,7 @@ namespace osu.Game.Tests.Online AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.TriggerSuccess(testBeatmapFile)); addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing); - AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true)); + AddStep("allow importing", () => beatmaps.AllowImport.Set()); AddUntilStep("wait for import", () => beatmaps.CurrentImport != null); AddUntilStep("ensure beatmap available", () => beatmaps.IsAvailableLocally(testBeatmapSet)); addAvailabilityCheckStep("state is locally available", BeatmapAvailability.LocallyAvailable); @@ -141,7 +140,7 @@ namespace osu.Game.Tests.Online [Test] public void TestTrackerRespectsSoftDeleting() { - AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true)); + AddStep("allow importing", () => beatmaps.AllowImport.Set()); AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely()); addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable); @@ -155,7 +154,7 @@ namespace osu.Game.Tests.Online [Test] public void TestTrackerRespectsChecksum() { - AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true)); + AddStep("allow importing", () => beatmaps.AllowImport.Set()); AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely()); addAvailabilityCheckStep("initially locally available", BeatmapAvailability.LocallyAvailable); @@ -202,7 +201,7 @@ namespace osu.Game.Tests.Online private class TestBeatmapManager : BeatmapManager { - public TaskCompletionSource AllowImport = new TaskCompletionSource(); + public readonly ManualResetEventSlim AllowImport = new ManualResetEventSlim(); public Live CurrentImport { get; private set; } @@ -229,7 +228,9 @@ namespace osu.Game.Tests.Online public override Live ImportModel(BeatmapSetInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default) { - testBeatmapManager.AllowImport.Task.WaitSafely(); + if (!testBeatmapManager.AllowImport.Wait(TimeSpan.FromSeconds(10), cancellationToken)) + throw new TimeoutException("Timeout waiting for import to be allowed."); + return (testBeatmapManager.CurrentImport = base.ImportModel(item, archive, batchImport, cancellationToken)); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs index c852685b74..60ed0012ae 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs @@ -66,7 +66,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddSliderStep("score", 0, 1000000, 500000, v => scoreProcessor.TotalScore.Value = v); AddSliderStep("accuracy", 0f, 1f, 0.5f, v => scoreProcessor.Accuracy.Value = v); - AddSliderStep("combo", 0, 1000, 0, v => scoreProcessor.HighestCombo.Value = v); + AddSliderStep("combo", 0, 10000, 0, v => scoreProcessor.HighestCombo.Value = v); + AddStep("toggle expanded", () => leaderboard.Expanded.Value = !leaderboard.Expanded.Value); } [Test] diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 5e76fe1519..003cec0d07 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -9,8 +9,10 @@ using NUnit.Framework; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; @@ -92,6 +94,31 @@ namespace osu.Game.Tests.Visual.Navigation returnToMenu(); } + [Test] + public void TestFromSongSelectWithFilter([Values] ScorePresentType type) + { + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); + + AddStep("filter to nothing", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).FilterControl.CurrentTextSearch.Value = "fdsajkl;fgewq"); + AddUntilStep("wait for no results", () => Beatmap.IsDefault); + + var firstImport = importScore(1, new CatchRuleset().RulesetInfo); + presentAndConfirm(firstImport, type); + } + + [Test] + public void TestFromSongSelectWithConvertRulesetChange([Values] ScorePresentType type) + { + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); + + AddStep("set convert to false", () => Game.LocalConfig.SetValue(OsuSetting.ShowConvertedBeatmaps, false)); + + var firstImport = importScore(1, new CatchRuleset().RulesetInfo); + presentAndConfirm(firstImport, type); + } + [Test] public void TestFromSongSelect([Values] ScorePresentType type) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 59c0af1533..939d3a63ed 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -561,9 +561,11 @@ namespace osu.Game return; } + // This should be able to be performed from song select, but that is disabled for now + // due to the weird decoupled ruleset logic (which can cause a crash in certain filter scenarios). PerformFromScreen(screen => { - Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset} to match score"); + Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset}) to match score"); Ruleset.Value = databasedScore.ScoreInfo.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); @@ -578,7 +580,7 @@ namespace osu.Game screen.Push(new SoloResultsScreen(databasedScore.ScoreInfo, false)); break; } - }, validScreens: new[] { typeof(PlaySongSelect) }); + }); } public override Task Import(params ImportTask[] imports) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 28642f12a1..c64a3101b7 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics windowModes.BindTo(host.Window.SupportedWindowModes); } - if (host.Window is WindowsWindow windowsWindow) - fullscreenCapability.BindTo(windowsWindow.FullscreenCapability); + if (host.Renderer is IWindowsRenderer windowsRenderer) + fullscreenCapability.BindTo(windowsRenderer.FullscreenCapability); Children = new Drawable[] { diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 29354e610d..2eec8253b3 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -39,8 +40,6 @@ namespace osu.Game.Screens.Play.HUD private const float rank_text_width = 35f; - private const float score_components_width = 85f; - private const float avatar_size = 25f; private const double panel_transition_duration = 500; @@ -161,7 +160,7 @@ namespace osu.Game.Screens.Play.HUD { new Dimension(GridSizeMode.Absolute, rank_text_width), new Dimension(), - new Dimension(GridSizeMode.AutoSize, maxSize: score_components_width), + new Dimension(GridSizeMode.AutoSize), }, Content = new[] { @@ -286,8 +285,19 @@ namespace osu.Game.Screens.Play.HUD LoadComponentAsync(new DrawableAvatar(User), avatarContainer.Add); TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true); - Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true); - Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true); + + Accuracy.BindValueChanged(v => + { + accuracyText.Text = v.NewValue.FormatAccuracy(); + updateDetailsWidth(); + }, true); + + Combo.BindValueChanged(v => + { + comboText.Text = $"{v.NewValue}x"; + updateDetailsWidth(); + }, true); + HasQuit.BindValueChanged(_ => updateState()); } @@ -303,13 +313,10 @@ namespace osu.Game.Screens.Play.HUD private void changeExpandedState(ValueChangedEvent expanded) { - scoreComponents.ClearTransforms(); - if (expanded.NewValue) { gridContainer.ResizeWidthTo(regular_width, panel_transition_duration, Easing.OutQuint); - scoreComponents.ResizeWidthTo(score_components_width, panel_transition_duration, Easing.OutQuint); scoreComponents.FadeIn(panel_transition_duration, Easing.OutQuint); usernameText.FadeIn(panel_transition_duration, Easing.OutQuint); @@ -318,11 +325,29 @@ namespace osu.Game.Screens.Play.HUD { gridContainer.ResizeWidthTo(compact_width, panel_transition_duration, Easing.OutQuint); - scoreComponents.ResizeWidthTo(0, panel_transition_duration, Easing.OutQuint); scoreComponents.FadeOut(text_transition_duration, Easing.OutQuint); usernameText.FadeOut(text_transition_duration, Easing.OutQuint); } + + updateDetailsWidth(); + } + + private float? scoreComponentsTargetWidth; + + private void updateDetailsWidth() + { + const float score_components_min_width = 88f; + + float newWidth = Expanded.Value + ? Math.Max(score_components_min_width, comboText.DrawWidth + accuracyText.DrawWidth + 25) + : 0; + + if (scoreComponentsTargetWidth == newWidth) + return; + + scoreComponentsTargetWidth = newWidth; + scoreComponents.ResizeWidthTo(newWidth, panel_transition_duration, Easing.OutQuint); } private void updateState() diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 1f56915f62..aad7fdff39 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -138,7 +138,8 @@ namespace osu.Game.Screens.Select return false; } - TriggerClick(); + if (!e.Repeat) + TriggerClick(); return true; } diff --git a/osu.Game/Screens/Utility/LatencyCertifierScreen.cs b/osu.Game/Screens/Utility/LatencyCertifierScreen.cs index c9d4dc7811..bacaccd68e 100644 --- a/osu.Game/Screens/Utility/LatencyCertifierScreen.cs +++ b/osu.Game/Screens/Utility/LatencyCertifierScreen.cs @@ -261,8 +261,8 @@ namespace osu.Game.Screens.Utility string exclusive = "unknown"; - if (host.Window is WindowsWindow windowsWindow) - exclusive = windowsWindow.FullscreenCapability.ToString(); + if (host.Renderer is IWindowsRenderer windowsRenderer) + exclusive = windowsRenderer.FullscreenCapability.ToString(); statusText.Clear(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 434db87a80..26738673c8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index f395eab23c..af1e21edab 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,8 +61,8 @@ - + @@ -82,7 +82,7 @@ - +