From 857e943b8d5b3924378a1de65804d6fdc4c5467d Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 11 Sep 2022 17:30:14 +0800 Subject: [PATCH 001/295] hide catchcombo when Hud hide --- .../Legacy/LegacyCatchComboCounter.cs | 2 - .../UI/CatchComboDisplay.cs | 64 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs index b2dd29841b..b4d29988d9 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Catch.UI; diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs index e9c289e46a..a923aca2c8 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs @@ -4,9 +4,13 @@ #nullable disable using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK.Graphics; @@ -22,11 +26,69 @@ namespace osu.Game.Rulesets.Catch.UI [CanBeNull] public ICatchComboCounter ComboCounter => Drawable as ICatchComboCounter; + private Bindable hudVisibilityMode = null!; + + private readonly BindableBool replayLoaded = new BindableBool(); + + private readonly BindableBool showCombo = new BindableBool(); + + [Resolved] + private OsuConfigManager config { get; set; } + public CatchComboDisplay() : base(new CatchSkinComponent(CatchSkinComponents.CatchComboCounter), _ => Empty()) { } + [BackgroundDependencyLoader(true)] + private void load(DrawableRuleset drawableRuleset) + { + hudVisibilityMode = config.GetBindable(OsuSetting.HUDVisibilityMode); + + hudVisibilityMode.BindValueChanged(s => + { + updateVisibilityState(); + }); + + if (drawableRuleset != null) + replayLoaded.BindTo(drawableRuleset.HasReplayLoaded); + + replayLoaded.BindValueChanged(s => + { + updateVisibilityState(); + }); + + showCombo.BindValueChanged(s => + { + if (ComboCounter == null) return; + + if (!s.NewValue) + { + ComboCounter.Hide(); + } + }); + + updateVisibilityState(); + + void updateVisibilityState() + { + switch (hudVisibilityMode.Value) + { + case HUDVisibilityMode.Never: + showCombo.Value = false; + break; + + case HUDVisibilityMode.HideDuringGameplay: + showCombo.Value = replayLoaded.Value; + break; + + case HUDVisibilityMode.Always: + showCombo.Value = true; + break; + } + } + } + protected override void SkinChanged(ISkinSource skin) { base.SkinChanged(skin); @@ -57,6 +119,8 @@ namespace osu.Game.Rulesets.Catch.UI private void updateCombo(int newCombo, Color4? hitObjectColour) { + if (!showCombo.Value) return; + currentCombo = newCombo; ComboCounter?.UpdateCombo(newCombo, hitObjectColour); } From 2bd72fd9b439f6b723ed43f963b7b5db62daf8d6 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Mon, 12 Sep 2022 17:53:38 +0800 Subject: [PATCH 002/295] add test for hide catchercombo --- .../TestSceneComboCounter.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs index 7f513728af..f39af76a2d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs @@ -1,12 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.UI; @@ -19,15 +19,25 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneComboCounter : CatchSkinnableTestScene { - private ScoreProcessor scoreProcessor; + private OsuConfigManager localConfig = null!; + + private ScoreProcessor scoreProcessor = null!; private Color4 judgedObjectColour = Color4.White; + [BackgroundDependencyLoader] + private void load() + { + Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); + } + [SetUp] public void SetUp() => Schedule(() => { scoreProcessor = new ScoreProcessor(new CatchRuleset()); + localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); + SetContents(_ => new CatchComboDisplay { Anchor = Anchor.Centre, @@ -51,9 +61,15 @@ namespace osu.Game.Rulesets.Catch.Tests 1f ); }); + + AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); + AddRepeatStep("perform hit", () => performJudgement(HitResult.Great), 5); + + AddStep("set hud to show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always)); + AddRepeatStep("perform hit", () => performJudgement(HitResult.Great), 5); } - private void performJudgement(HitResult type, Judgement judgement = null) + private void performJudgement(HitResult type, Judgement? judgement = null) { var judgedObject = new DrawableFruit(new Fruit()) { AccentColour = { Value = judgedObjectColour } }; From c0e2ba419e60bf72affedb4dcb5a8d0cc7f73424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A2=E3=82=BA=E3=82=BF=E3=82=B1?= Date: Fri, 16 Sep 2022 18:31:02 +0900 Subject: [PATCH 003/295] Localize non-localizable setting items --- osu.Game/Localisation/DebugSettingsStrings.cs | 10 ++ .../Localisation/GeneralSettingsStrings.cs | 5 + .../MaintenanceSettingsStrings.cs | 111 ++++++++++++++++++ .../Localisation/TabletSettingsStrings.cs | 5 + .../Sections/DebugSettings/MemorySettings.cs | 4 +- .../Sections/General/UpdateSettings.cs | 2 +- .../Settings/Sections/Input/TabletSettings.cs | 7 +- .../Sections/Maintenance/BeatmapSettings.cs | 2 +- .../Maintenance/CollectionsSettings.cs | 4 +- .../MassDeleteConfirmationDialog.cs | 3 +- .../MassVideoDeleteConfirmationDialog.cs | 3 +- .../Maintenance/MigrationRunScreen.cs | 9 +- .../Maintenance/MigrationSelectScreen.cs | 7 +- .../Sections/Maintenance/ModPresetSettings.cs | 6 +- .../Sections/Maintenance/ScoreSettings.cs | 2 +- .../Sections/Maintenance/SkinSettings.cs | 2 +- .../StableDirectoryLocationDialog.cs | 5 +- .../StableDirectorySelectScreen.cs | 3 +- 18 files changed, 163 insertions(+), 27 deletions(-) diff --git a/osu.Game/Localisation/DebugSettingsStrings.cs b/osu.Game/Localisation/DebugSettingsStrings.cs index 74b2c8d892..66ce0fa109 100644 --- a/osu.Game/Localisation/DebugSettingsStrings.cs +++ b/osu.Game/Localisation/DebugSettingsStrings.cs @@ -49,6 +49,16 @@ namespace osu.Game.Localisation /// public static LocalisableString CompactRealm => new TranslatableString(getKey(@"compact_realm"), @"Compact realm"); + /// + /// "Block realm" + /// + public static LocalisableString BlockRealm => new TranslatableString(getKey(@"block_realm"), @"Block realm"); + + /// + /// "Unblock realm" + /// + public static LocalisableString UnblockRealm => new TranslatableString(getKey(@"unblock_realm"), @"Unblock realm"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index 2aa91f5245..bbad80976e 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -64,6 +64,11 @@ namespace osu.Game.Localisation /// public static LocalisableString RunSetupWizard => new TranslatableString(getKey(@"run_setup_wizard"), @"Run setup wizard"); + /// + /// "You are running the latest release ({0})" + /// + public static LocalisableString RunningLatestRelease(string arg0) => new TranslatableString(getKey(@"running_latest_release"), @"You are running the latest release ({0})", arg0); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Localisation/MaintenanceSettingsStrings.cs b/osu.Game/Localisation/MaintenanceSettingsStrings.cs index a398eced07..8b7ca8d93a 100644 --- a/osu.Game/Localisation/MaintenanceSettingsStrings.cs +++ b/osu.Game/Localisation/MaintenanceSettingsStrings.cs @@ -14,11 +14,72 @@ namespace osu.Game.Localisation /// public static LocalisableString MaintenanceSectionHeader => new TranslatableString(getKey(@"maintenance_section_header"), @"Maintenance"); + /// + /// "Beatmaps" + /// + public static LocalisableString Beatmaps => new TranslatableString(getKey(@"beatmaps"), @"Beatmaps"); + + /// + /// "Skins" + /// + public static LocalisableString Skins => new TranslatableString(getKey(@"skins"), @"Skins"); + + /// + /// "Collections" + /// + public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections"); + + + /// + /// "Scores" + /// + public static LocalisableString Scores => new TranslatableString(getKey(@"scores"), @"Scores"); + + /// + /// "Mod presets" + /// + public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"Mod presets"); + /// /// "Select directory" /// public static LocalisableString SelectDirectory => new TranslatableString(getKey(@"select_directory"), @"Select directory"); + /// + /// "Migration in progress" + /// + public static LocalisableString MigrationInProgress => new TranslatableString(getKey(@"migration_in_progress"), @"Migration in progress"); + + /// + /// "This could take a few minutes depending on the speed of your disk(s)." + /// + public static LocalisableString MigrationDescription => new TranslatableString(getKey(@"migration_description"), @"This could take a few minutes depending on the speed of your disk(s)."); + + /// + /// "Please avoid interacting with the game!" + /// + public static LocalisableString ProhibitedInteractDuringMigration => new TranslatableString(getKey(@"prohibited_interact_during_migration"), @"Please avoid interacting with the game!"); + + /// + /// "Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up." + /// + public static LocalisableString FailedCleanupNotification => new TranslatableString(getKey(@"failed_cleanup_notification"), @"Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up."); + + /// + /// "Please select a new location" + /// + public static LocalisableString SelectNewLocation => new TranslatableString(getKey(@"select_new_location"), @"Please select a new location"); + + /// + /// "The target directory already seems to have an osu! install. Use that data instead?" + /// + public static LocalisableString TargetDirectoryAlreadyInstalledOsu => new TranslatableString(getKey(@"target_directory_already_installed_osu"), @"The target directory already seems to have an osu! install. Use that data instead?"); + + /// + /// "To complete this operation, osu! will close. Please open it again to use the new data location." + /// + public static LocalisableString RestartAndReOpenRequiredForCompletion => new TranslatableString(getKey(@"restart_and_re_open_required_for_completion"), @"To complete this operation, osu! will close. Please open it again to use the new data location."); + /// /// "Import beatmaps from stable" /// @@ -84,6 +145,56 @@ namespace osu.Game.Localisation /// public static LocalisableString RestoreAllRecentlyDeletedModPresets => new TranslatableString(getKey(@"restore_all_recently_deleted_mod_presets"), @"Restore all recently deleted mod presets"); + /// + /// "Deleted all collections!" + /// + public static LocalisableString DeletedAllCollections => new TranslatableString(getKey(@"deleted_all_collections"), @"Deleted all collections!"); + + /// + /// "Deleted all mod presets!" + /// + public static LocalisableString DeletedAllModPresets => new TranslatableString(getKey(@"deleted_all_mod_presets"), @"Deleted all mod presets!"); + + /// + /// "Restored all deleted mod presets!" + /// + public static LocalisableString RestoredAllDeletedModPresets => new TranslatableString(getKey(@"restored_all_deleted_mod_presets"), @"Restored all deleted mod presets!"); + + /// + /// "Everything?" + /// + public static LocalisableString MassDeleteConfirmation => new TranslatableString(getKey(@"mass_delete_confirmation"), @"Everything?"); + + /// + /// "All beatmap videos? This cannot be undone!" + /// + public static LocalisableString MassVideoDeleteConfirmation => new TranslatableString(getKey(@"mass_video_delete_confirmation"), @"All beatmap videos? This cannot be undone!"); + + /// + /// "Failed to automatically locate an osu!stable installation." + /// + public static LocalisableString StableDirectoryLocationHeader => new TranslatableString(getKey(@"stable_directory_location_header"), @"Failed to automatically locate an osu!stable installation."); + + /// + /// "An existing install could not be located. If you know where it is, you can help locate it." + /// + public static LocalisableString StableDirectoryLocationBody => new TranslatableString(getKey(@"stable_directory_location_body"), @"An existing install could not be located. If you know where it is, you can help locate it."); + + /// + /// "Sure! I know where it is located!" + /// + public static LocalisableString StableDirectoryLocationOk => new TranslatableString(getKey(@"stable_directory_location_ok"), @"Sure! I know where it is located!"); + + /// + /// "Actually I don't have osu!stable installed." + /// + public static LocalisableString StableDirectoryLocationCancel => new TranslatableString(getKey(@"stable_directory_location_cancel"), @"Actually I don't have osu!stable installed."); + + /// + /// "Please select your osu!stable install location" + /// + public static LocalisableString StableDirectorySelectHeader => new TranslatableString(getKey(@"stable_directory_select_header"), @"Please select your osu!stable install location"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Localisation/TabletSettingsStrings.cs b/osu.Game/Localisation/TabletSettingsStrings.cs index d62d348df9..6c2e3c1f9c 100644 --- a/osu.Game/Localisation/TabletSettingsStrings.cs +++ b/osu.Game/Localisation/TabletSettingsStrings.cs @@ -19,6 +19,11 @@ namespace osu.Game.Localisation /// public static LocalisableString NoTabletDetected => new TranslatableString(getKey(@"no_tablet_detected"), @"No tablet detected!"); + /// + /// "If your tablet is not detected, please read [this FAQ]({0}) for troubleshooting steps." + /// + public static LocalisableString NoTabletDetectedDescription(string url) => new TranslatableString(getKey(@"no_tablet_detected_description"), @"If your tablet is not detected, please read [this FAQ]({0}) for troubleshooting steps.", url); + /// /// "Reset to full area" /// diff --git a/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs b/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs index 42ac4adb34..5ec09adfda 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings }, blockAction = new SettingsButton { - Text = "Block realm", + Text = DebugSettingsStrings.BlockRealm, }, unblockAction = new SettingsButton { - Text = "Unblock realm", + Text = DebugSettingsStrings.UnblockRealm, }, }; diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index b68a4fed48..d97cf699e5 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { notifications?.Post(new SimpleNotification { - Text = $"You are running the latest release ({game.Version})", + Text = GeneralSettingsStrings.RunningLatestRelease(game.Version), Icon = FontAwesome.Solid.CheckCircle, }); } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 271438ed14..43676c5bbe 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -110,11 +110,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux) { t.NewLine(); - t.AddText("If your tablet is not detected, please read "); - t.AddLink("this FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows + var formattedSource = MessageFormatter.FormatText(TabletSettingsStrings.NoTabletDetectedDescription(RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? @"https://opentabletdriver.net/Wiki/FAQ/Windows" - : @"https://opentabletdriver.net/Wiki/FAQ/Linux"); - t.AddText(" for troubleshooting steps."); + : @"https://opentabletdriver.net/Wiki/FAQ/Linux").ToString()); + t.AddLinks(formattedSource.Text, formattedSource.Links); } }), } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs index 453dbd2e18..00342faf3b 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class BeatmapSettings : SettingsSubsection { - protected override LocalisableString Header => "Beatmaps"; + protected override LocalisableString Header => MaintenanceSettingsStrings.Beatmaps; private SettingsButton importBeatmapsButton = null!; private SettingsButton deleteBeatmapsButton = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs index 5a91213eb8..9ec6f59fb5 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class CollectionsSettings : SettingsSubsection { - protected override LocalisableString Header => "Collections"; + protected override LocalisableString Header => MaintenanceSettingsStrings.Collections; private SettingsButton importCollectionsButton = null!; @@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private void deleteAllCollections() { realm.Write(r => r.RemoveAll()); - notificationOverlay?.Post(new ProgressCompletionNotification { Text = "Deleted all collections!" }); + notificationOverlay?.Post(new ProgressCompletionNotification { Text = MaintenanceSettingsStrings.DeletedAllCollections }); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs index 19e6f83dac..bcfccaa5e2 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Localisation; using osu.Game.Overlays.Dialog; namespace osu.Game.Overlays.Settings.Sections.Maintenance @@ -10,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public MassDeleteConfirmationDialog(Action deleteAction) { - BodyText = "Everything?"; + BodyText = MaintenanceSettingsStrings.MassDeleteConfirmation; DeleteAction = deleteAction; } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs index fc8c9d497b..a386c64806 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -10,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public MassVideoDeleteConfirmationDialog(Action deleteAction) : base(deleteAction) { - BodyText = "All beatmap videos? This cannot be undone!"; + BodyText = MaintenanceSettingsStrings.MassVideoDeleteConfirmation; } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index d565576d09..158e1a8aa0 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -15,6 +15,7 @@ using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osu.Game.Overlays.Notifications; using osu.Game.Screens; using osuTK; @@ -71,14 +72,14 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = "Migration in progress", + Text = MaintenanceSettingsStrings.MigrationInProgress, Font = OsuFont.Default.With(size: 40) }, new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = "This could take a few minutes depending on the speed of your disk(s).", + Text = MaintenanceSettingsStrings.MigrationDescription, Font = OsuFont.Default.With(size: 30) }, new LoadingSpinner(true) @@ -89,7 +90,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = "Please avoid interacting with the game!", + Text = MaintenanceSettingsStrings.ProhibitedInteractDuringMigration, Font = OsuFont.Default.With(size: 30) }, } @@ -111,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { notifications.Post(new SimpleNotification { - Text = "Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up.", + Text = MaintenanceSettingsStrings.FailedCleanupNotification, Activated = () => { originalStorage.PresentExternally(); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs index 0d32e33d87..2f4f04fbc2 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -12,6 +12,7 @@ using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.IO; +using osu.Game.Localisation; using osu.Game.Overlays.Dialog; namespace osu.Game.Overlays.Settings.Sections.Maintenance @@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public override bool HideOverlaysOnEnter => true; - public override LocalisableString HeaderText => "Please select a new location"; + public override LocalisableString HeaderText => MaintenanceSettingsStrings.SelectNewLocation; protected override void OnSelection(DirectoryInfo directory) { @@ -51,9 +52,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance // Quick test for whether there's already an osu! install at the target path. if (fileInfos.Any(f => f.Name == OsuGameBase.CLIENT_DATABASE_FILENAME)) { - dialogOverlay.Push(new ConfirmDialog("The target directory already seems to have an osu! install. Use that data instead?", () => + dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.TargetDirectoryAlreadyInstalledOsu.ToString(), () => { - dialogOverlay.Push(new ConfirmDialog("To complete this operation, osu! will close. Please open it again to use the new data location.", () => + dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.RestartAndReOpenRequiredForCompletion.ToString(), () => { (storage as OsuStorage)?.ChangeDataPath(target.FullName); game.Exit(); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs index d35d3ff468..3e3138f041 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class ModPresetSettings : SettingsSubsection { - protected override LocalisableString Header => "Mod presets"; + protected override LocalisableString Header => MaintenanceSettingsStrings.ModPresets; [Resolved] private RealmAccess realm { get; set; } = null!; @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance deleteAllButton.Enabled.Value = true; if (deletionTask.IsCompletedSuccessfully) - notificationOverlay?.Post(new ProgressCompletionNotification { Text = "Deleted all mod presets!" }); + notificationOverlay?.Post(new ProgressCompletionNotification { Text = MaintenanceSettingsStrings.DeletedAllModPresets }); else if (deletionTask.IsFaulted) Logger.Error(deletionTask.Exception, "Failed to delete all mod presets"); } @@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance undeleteButton.Enabled.Value = true; if (undeletionTask.IsCompletedSuccessfully) - notificationOverlay?.Post(new ProgressCompletionNotification { Text = "Restored all deleted mod presets!" }); + notificationOverlay?.Post(new ProgressCompletionNotification { Text = MaintenanceSettingsStrings.RestoredAllDeletedModPresets }); else if (undeletionTask.IsFaulted) Logger.Error(undeletionTask.Exception, "Failed to restore mod presets"); } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs index 70e11d45dc..6377d59e2a 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class ScoreSettings : SettingsSubsection { - protected override LocalisableString Header => "Scores"; + protected override LocalisableString Header => MaintenanceSettingsStrings.Scores; private SettingsButton importScoresButton = null!; private SettingsButton deleteScoresButton = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs index c95b1d4dd8..893981f3d9 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class SkinSettings : SettingsSubsection { - protected override LocalisableString Header => "Skins"; + protected override LocalisableString Header => MaintenanceSettingsStrings.Skins; private SettingsButton importSkinsButton = null!; private SettingsButton deleteSkinsButton = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 8aff4520b5..31e5b05596 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; +using osu.Game.Localisation; using osu.Game.Overlays.Dialog; using osu.Game.Screens; @@ -19,8 +20,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { - HeaderText = "Failed to automatically locate an osu!stable installation."; - BodyText = "An existing install could not be located. If you know where it is, you can help locate it."; + HeaderText = MaintenanceSettingsStrings.StableDirectoryLocationHeader; + BodyText = MaintenanceSettingsStrings.StableDirectoryLocationBody; Icon = FontAwesome.Solid.QuestionCircle; Buttons = new PopupDialogButton[] diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 047d589689..22cf2e7076 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Localisation; using osu.Framework.Screens; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; - public override LocalisableString HeaderText => "Please select your osu!stable install location"; + public override LocalisableString HeaderText => MaintenanceSettingsStrings.StableDirectorySelectHeader; public StableDirectorySelectScreen(TaskCompletionSource taskCompletionSource) { From b99b10e586e79367459983aa45aac55e33cd9654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A2=E3=82=BA=E3=82=BF=E3=82=B1?= Date: Fri, 16 Sep 2022 18:34:13 +0900 Subject: [PATCH 004/295] fix code style problem --- osu.Game/Localisation/MaintenanceSettingsStrings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Localisation/MaintenanceSettingsStrings.cs b/osu.Game/Localisation/MaintenanceSettingsStrings.cs index 8b7ca8d93a..4d7fdc60f7 100644 --- a/osu.Game/Localisation/MaintenanceSettingsStrings.cs +++ b/osu.Game/Localisation/MaintenanceSettingsStrings.cs @@ -29,7 +29,6 @@ namespace osu.Game.Localisation /// public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections"); - /// /// "Scores" /// From 3a62d292698aa279f765c154e6004317e3d92fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A2=E3=82=BA=E3=82=BF=E3=82=B1?= Date: Fri, 16 Sep 2022 18:43:59 +0900 Subject: [PATCH 005/295] fix tab spacing --- osu.Game/Localisation/GeneralSettingsStrings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index bbad80976e..8506971756 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -65,9 +65,9 @@ namespace osu.Game.Localisation public static LocalisableString RunSetupWizard => new TranslatableString(getKey(@"run_setup_wizard"), @"Run setup wizard"); /// - /// "You are running the latest release ({0})" - /// - public static LocalisableString RunningLatestRelease(string arg0) => new TranslatableString(getKey(@"running_latest_release"), @"You are running the latest release ({0})", arg0); + /// "You are running the latest release ({0})" + /// + public static LocalisableString RunningLatestRelease(string arg0) => new TranslatableString(getKey(@"running_latest_release"), @"You are running the latest release ({0})", arg0); private static string getKey(string key) => $"{prefix}:{key}"; } From 81d582c051280ee3dbe6de607d654c7788ac7ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A2=E3=82=BA=E3=82=BF=E3=82=B1?= Date: Fri, 16 Sep 2022 21:08:25 +0900 Subject: [PATCH 006/295] fix review points and fine tuning --- osu.Game/Localisation/CommonStrings.cs | 5 ++++ osu.Game/Localisation/DebugSettingsStrings.cs | 15 ----------- .../MaintenanceSettingsStrings.cs | 25 ------------------- osu.Game/Overlays/Dialog/ConfirmDialog.cs | 3 ++- .../Sections/DebugSettings/MemorySettings.cs | 6 ++--- .../Settings/Sections/Input/TabletSettings.cs | 6 ++--- .../Sections/Maintenance/BeatmapSettings.cs | 2 +- .../Maintenance/CollectionsSettings.cs | 2 +- .../Maintenance/MigrationSelectScreen.cs | 4 +-- .../Sections/Maintenance/ModPresetSettings.cs | 2 +- .../Sections/Maintenance/ScoreSettings.cs | 2 +- .../Sections/Maintenance/SkinSettings.cs | 2 +- .../StableDirectoryLocationDialog.cs | 4 +-- 13 files changed, 22 insertions(+), 56 deletions(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 93e3276f59..385ebd0593 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -89,6 +89,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections"); + /// + /// "Mod presets" + /// + public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"Mod presets"); + /// /// "Name" /// diff --git a/osu.Game/Localisation/DebugSettingsStrings.cs b/osu.Game/Localisation/DebugSettingsStrings.cs index 66ce0fa109..dd21739096 100644 --- a/osu.Game/Localisation/DebugSettingsStrings.cs +++ b/osu.Game/Localisation/DebugSettingsStrings.cs @@ -44,21 +44,6 @@ namespace osu.Game.Localisation /// public static LocalisableString ClearAllCaches => new TranslatableString(getKey(@"clear_all_caches"), @"Clear all caches"); - /// - /// "Compact realm" - /// - public static LocalisableString CompactRealm => new TranslatableString(getKey(@"compact_realm"), @"Compact realm"); - - /// - /// "Block realm" - /// - public static LocalisableString BlockRealm => new TranslatableString(getKey(@"block_realm"), @"Block realm"); - - /// - /// "Unblock realm" - /// - public static LocalisableString UnblockRealm => new TranslatableString(getKey(@"unblock_realm"), @"Unblock realm"); - private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Localisation/MaintenanceSettingsStrings.cs b/osu.Game/Localisation/MaintenanceSettingsStrings.cs index 4d7fdc60f7..4648682e64 100644 --- a/osu.Game/Localisation/MaintenanceSettingsStrings.cs +++ b/osu.Game/Localisation/MaintenanceSettingsStrings.cs @@ -14,31 +14,6 @@ namespace osu.Game.Localisation /// public static LocalisableString MaintenanceSectionHeader => new TranslatableString(getKey(@"maintenance_section_header"), @"Maintenance"); - /// - /// "Beatmaps" - /// - public static LocalisableString Beatmaps => new TranslatableString(getKey(@"beatmaps"), @"Beatmaps"); - - /// - /// "Skins" - /// - public static LocalisableString Skins => new TranslatableString(getKey(@"skins"), @"Skins"); - - /// - /// "Collections" - /// - public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections"); - - /// - /// "Scores" - /// - public static LocalisableString Scores => new TranslatableString(getKey(@"scores"), @"Scores"); - - /// - /// "Mod presets" - /// - public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"Mod presets"); - /// /// "Select directory" /// diff --git a/osu.Game/Overlays/Dialog/ConfirmDialog.cs b/osu.Game/Overlays/Dialog/ConfirmDialog.cs index 8be865ee16..c17080f602 100644 --- a/osu.Game/Overlays/Dialog/ConfirmDialog.cs +++ b/osu.Game/Overlays/Dialog/ConfirmDialog.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dialog @@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Dialog /// The description of the action to be displayed to the user. /// An action to perform on confirmation. /// An optional action to perform on cancel. - public ConfirmDialog(string message, Action onConfirm, Action onCancel = null) + public ConfirmDialog(LocalisableString message, Action onConfirm, Action onCancel = null) { HeaderText = message; BodyText = "Last chance to turn back"; diff --git a/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs b/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs index 5ec09adfda..3afb060e49 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings }, new SettingsButton { - Text = DebugSettingsStrings.CompactRealm, + Text = "Compact realm", Action = () => { // Blocking operations implicitly causes a Compact(). @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings }, blockAction = new SettingsButton { - Text = DebugSettingsStrings.BlockRealm, + Text = "Block realm", }, unblockAction = new SettingsButton { - Text = DebugSettingsStrings.UnblockRealm, + Text = "Unblock realm", }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 43676c5bbe..e32639f476 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -72,7 +72,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, LocalisationManager localisation) { Children = new Drawable[] { @@ -110,9 +110,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux) { t.NewLine(); - var formattedSource = MessageFormatter.FormatText(TabletSettingsStrings.NoTabletDetectedDescription(RuntimeInfo.OS == RuntimeInfo.Platform.Windows + var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedBindableString(TabletSettingsStrings.NoTabletDetectedDescription(RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? @"https://opentabletdriver.net/Wiki/FAQ/Windows" - : @"https://opentabletdriver.net/Wiki/FAQ/Linux").ToString()); + : @"https://opentabletdriver.net/Wiki/FAQ/Linux")).Value); t.AddLinks(formattedSource.Text, formattedSource.Links); } }), diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs index 00342faf3b..beae5a6aad 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class BeatmapSettings : SettingsSubsection { - protected override LocalisableString Header => MaintenanceSettingsStrings.Beatmaps; + protected override LocalisableString Header => CommonStrings.Beatmaps; private SettingsButton importBeatmapsButton = null!; private SettingsButton deleteBeatmapsButton = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs index 9ec6f59fb5..17fef37e40 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class CollectionsSettings : SettingsSubsection { - protected override LocalisableString Header => MaintenanceSettingsStrings.Collections; + protected override LocalisableString Header => CommonStrings.Collections; private SettingsButton importCollectionsButton = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs index 2f4f04fbc2..5de33fdd55 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -52,9 +52,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance // Quick test for whether there's already an osu! install at the target path. if (fileInfos.Any(f => f.Name == OsuGameBase.CLIENT_DATABASE_FILENAME)) { - dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.TargetDirectoryAlreadyInstalledOsu.ToString(), () => + dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.TargetDirectoryAlreadyInstalledOsu, () => { - dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.RestartAndReOpenRequiredForCompletion.ToString(), () => + dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.RestartAndReOpenRequiredForCompletion, () => { (storage as OsuStorage)?.ChangeDataPath(target.FullName); game.Exit(); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs index 3e3138f041..51f6e1bf60 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class ModPresetSettings : SettingsSubsection { - protected override LocalisableString Header => MaintenanceSettingsStrings.ModPresets; + protected override LocalisableString Header => CommonStrings.ModPresets; [Resolved] private RealmAccess realm { get; set; } = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs index 6377d59e2a..eb2d3171ea 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class ScoreSettings : SettingsSubsection { - protected override LocalisableString Header => MaintenanceSettingsStrings.Scores; + protected override LocalisableString Header => CommonStrings.Scores; private SettingsButton importScoresButton = null!; private SettingsButton deleteScoresButton = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs index 893981f3d9..93c65513b7 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class SkinSettings : SettingsSubsection { - protected override LocalisableString Header => MaintenanceSettingsStrings.Skins; + protected override LocalisableString Header => CommonStrings.Skins; private SettingsButton importSkinsButton = null!; private SettingsButton deleteSkinsButton = null!; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 31e5b05596..7b7ea7cee0 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -28,12 +28,12 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { new PopupDialogOkButton { - Text = "Sure! I know where it is located!", + Text = MaintenanceSettingsStrings.StableDirectoryLocationOk, Action = () => Schedule(() => performer.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) }, new PopupDialogCancelButton { - Text = "Actually I don't have osu!stable installed.", + Text = MaintenanceSettingsStrings.StableDirectoryLocationCancel, Action = () => taskCompletionSource.TrySetCanceled() } }; From 493efd84a3900f4c439887f5db931fb699d143f7 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 12:08:34 -0700 Subject: [PATCH 007/295] Basic smoke path implementation --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 5 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 1 + .../Skinning/Default/DefaultSmoke.cs | 14 ++ .../Skinning/Legacy/LegacySmoke.cs | 26 ++++ .../Legacy/OsuLegacySkinTransformer.cs | 6 + osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 131 ++++++++++++++++++ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 2 + osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 57 ++++++++ 9 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Smoke.cs create mode 100644 osu.Game.Rulesets.Osu/UI/SmokeContainer.cs diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 12256e93d0..dec965e567 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -80,6 +80,9 @@ namespace osu.Game.Rulesets.Osu LeftButton, [Description("Right button")] - RightButton + RightButton, + + [Description("Smoke")] + Smoke, } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 302194e91a..76465fe3d5 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.Osu { new KeyBinding(InputKey.Z, OsuAction.LeftButton), new KeyBinding(InputKey.X, OsuAction.RightButton), + new KeyBinding(InputKey.C, OsuAction.Smoke), new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), }; diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index fcf079b6aa..11daa26072 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Osu SliderBall, SliderBody, SpinnerBody, + Smoke, ApproachCircle, } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs new file mode 100644 index 0000000000..3c798c2afa --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Osu.Skinning.Default +{ + public class DefaultSmoke : Smoke + { + public DefaultSmoke() + { + Texture = null; + PathRadius = 2; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs new file mode 100644 index 0000000000..9494d31a63 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacySmoke : Smoke + { + private ISkin skin; + + public LegacySmoke(ISkin skin) + { + this.skin = skin; + + PathRadius = 8; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Texture = skin.GetTexture("cursor-smoke"); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 885a2c12fb..a9186f821e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -106,6 +106,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; + case OsuSkinComponents.Smoke: + if (GetTexture("cursor-smoke") != null) + return new LegacySmoke(this); + + return null; + case OsuSkinComponents.HitCircleText: if (!this.HasFont(LegacyFont.HitCircle)) return null; diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs new file mode 100644 index 0000000000..6c60392c94 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -0,0 +1,131 @@ +// 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.Diagnostics; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Lines; +using osu.Game.Rulesets.Osu.UI; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public abstract class Smoke : TexturedPath + { + protected bool IsActive { get; private set; } + protected double SmokeTimeStart { get; private set; } = double.MinValue; + protected double SmokeTimeEnd { get; private set; } = double.MinValue; + + protected readonly List SmokeVertexPositions = new List(); + protected readonly List SmokeVertexTimes = new List(); + + [Resolved(CanBeNull = true)] + private SmokeContainer? smokeContainer { get; set; } + + protected struct SmokePoint + { + public Vector2 Position; + public double Time; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (smokeContainer != null) + { + smokeContainer.SmokeMoved += guardOnSmokeMoved; + smokeContainer.SmokeEnded += guardOnSmokeEnded; + IsActive = true; + } + + Anchor = Anchor.TopLeft; + Origin = Anchor.TopLeft; + + SmokeTimeStart = Time.Current; + } + + private void guardOnSmokeMoved(Vector2 position, double time) + { + if (IsActive) + OnSmokeMoved(position, time); + } + + private void guardOnSmokeEnded(double time) + { + if (IsActive) + OnSmokeEnded(time); + } + + protected virtual void OnSmokeMoved(Vector2 position, double time) + { + addSmokeVertex(position, time); + } + + private void addSmokeVertex(Vector2 position, double time) + { + Debug.Assert(SmokeVertexTimes.Count == SmokeVertexPositions.Count); + + if (SmokeVertexTimes.Count > 0 && SmokeVertexTimes.Last() > time) + { + int index = ~SmokeVertexTimes.BinarySearch(time, new UpperBoundComparer()); + + SmokeVertexTimes.RemoveRange(index, SmokeVertexTimes.Count - index); + SmokeVertexPositions.RemoveRange(index, SmokeVertexPositions.Count - index); + } + + SmokeVertexTimes.Add(time); + SmokeVertexPositions.Add(position); + } + + protected virtual void OnSmokeEnded(double time) + { + IsActive = false; + SmokeTimeEnd = time; + } + + protected override void Update() + { + base.Update(); + + const double visible_duration = 8000; + const float disappear_speed = 3; + + int index = 0; + if (!IsActive) + { + double cutoffTime = SmokeTimeStart + disappear_speed * (Time.Current - (SmokeTimeEnd + visible_duration)); + index = ~SmokeVertexTimes.BinarySearch(cutoffTime, new UpperBoundComparer()); + } + Vertices = new List(SmokeVertexPositions.Skip(index)); + + Position = -PositionInBoundingBox(Vector2.Zero); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (smokeContainer != null) + { + smokeContainer.SmokeMoved -= guardOnSmokeMoved; + smokeContainer.SmokeEnded -= guardOnSmokeEnded; + } + } + + private struct UpperBoundComparer : IComparer + { + public int Compare(double x, double target) + { + // By returning -1 when the target value is equal to x, guarantees that the + // element at BinarySearch's returned index will always be the first element + // larger. Since 0 is never returned, the target is never "found", so the return + // value will be the index's complement. + + return x > target ? 1 : -1; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index fc2ba8ea2f..8085c07912 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Osu.UI public class OsuPlayfield : Playfield { private readonly PlayfieldBorder playfieldBorder; + private readonly SmokeContainer smokeContainer; private readonly ProxyContainer approachCircles; private readonly ProxyContainer spinnerProxies; private readonly JudgementContainer judgementLayer; @@ -54,6 +55,7 @@ namespace osu.Game.Rulesets.Osu.UI InternalChildren = new Drawable[] { playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both }, + smokeContainer = new SmokeContainer { RelativeSizeAxes = Axes.Both }, spinnerProxies = new ProxyContainer { RelativeSizeAxes = Axes.Both }, FollowPoints = new FollowPointRenderer { RelativeSizeAxes = Axes.Both }, judgementLayer = new JudgementContainer { RelativeSizeAxes = Axes.Both }, diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs new file mode 100644 index 0000000000..865204f8cc --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -0,0 +1,57 @@ +// 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.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Osu.Skinning.Default; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Osu.UI +{ + [Cached] + public class SmokeContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler + { + public event Action? SmokeMoved; + public event Action? SmokeEnded; + + private bool isSmoking = false; + + public override bool ReceivePositionalInputAt(Vector2 _) => true; + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == OsuAction.Smoke) + { + isSmoking = true; + AddInternal(new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Smoke), _ => new DefaultSmoke())); + + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + if (e.Action == OsuAction.Smoke) + { + isSmoking = false; + SmokeEnded?.Invoke(Time.Current); + } + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (isSmoking) + SmokeMoved?.Invoke(e.MousePosition, Time.Current); + + return base.OnMouseMove(e); + } + } +} From 613564b5b9ecdb56953aa093982b3c22a565f8e4 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 12:10:01 -0700 Subject: [PATCH 008/295] Full legacy smoke implementation and temp default smoke --- .../Skinning/Default/DefaultSmoke.cs | 51 +- .../Skinning/Legacy/LegacySmoke.cs | 93 +++- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 443 +++++++++++++++--- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 1 - 4 files changed, 521 insertions(+), 67 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index 3c798c2afa..ecba9d4904 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -1,14 +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; +using System.Diagnostics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Rendering; +using osuTK.Graphics; + namespace osu.Game.Rulesets.Osu.Skinning.Default { public class DefaultSmoke : Smoke { + private const double fade_out_delay = 8000; + private const double fade_out_speed = 3; + private const double fade_out_duration = 50; + private const float alpha = 0.5f; + + protected override double LifetimeAfterSmokeEnd => fade_out_delay + fade_out_duration + (SmokeEndTime - SmokeStartTime) / fade_out_speed; + public DefaultSmoke() { - Texture = null; - PathRadius = 2; + Radius = 2; + } + + protected override DrawNode CreateDrawNode() => new DefaultSmokeDrawNode(this); + + private class DefaultSmokeDrawNode : SmokeDrawNode + { + private double fadeOutTime; + + public DefaultSmokeDrawNode(ITexturedShaderDrawable source) + : base(source) + { + } + + protected override void UpdateDrawVariables(IRenderer renderer) + { + base.UpdateDrawVariables(renderer); + + fadeOutTime = SmokeStartTime + fade_out_speed * (CurrentTime - (SmokeEndTime + fade_out_delay)); + } + + protected override Color4 ColorAtTime(double pointTime) + { + var color = Color4.White; + color.A = alpha; + + double timeDoingFadeOut = fadeOutTime - pointTime; + if (timeDoingFadeOut > 0) + { + float fraction = Math.Clamp((float)(1 - (timeDoingFadeOut / fade_out_duration)), 0, 1); + fraction = MathF.Pow(fraction, 5); + color.A *= fraction; + } + + return color; + } } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index 9494d31a63..6c2c80f746 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -1,19 +1,42 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Diagnostics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Rendering; using osu.Game.Skinning; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacySmoke : Smoke { + private const double initial_fade_out_duration = 2500; + + private const double re_fade_in_speed = 3; + private const double re_fade_in_duration = 50; + + private const double final_fade_out_duration = 7500; + + private const float initial_alpha = 0.8f; + private const float re_fade_in_alpha = 1.4f; + + protected override double LifetimeAfterSmokeEnd + { + get + { + double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); + return final_fade_out_duration + initialFadeOutDurationTrunc * (1 + re_fade_in_speed); + } + } + private ISkin skin; public LegacySmoke(ISkin skin) { this.skin = skin; - - PathRadius = 8; + Radius = 3; } protected override void LoadComplete() @@ -22,5 +45,71 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Texture = skin.GetTexture("cursor-smoke"); } + + protected override DrawNode CreateDrawNode() => new LegacySmokeDrawNode(this); + + protected class LegacySmokeDrawNode : SmokeDrawNode + { + private double initialFadeOutDurationTrunc; + private double initialFadeOutTime; + private double reFadeInTime; + private double finalFadeOutTime; + + public LegacySmokeDrawNode(ITexturedShaderDrawable source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); + } + + protected override void UpdateDrawVariables(IRenderer renderer) + { + base.UpdateDrawVariables(renderer); + + initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime); + reFadeInTime = re_fade_in_speed * (CurrentTime - SmokeEndTime) + SmokeEndTime - initialFadeOutDurationTrunc; + finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc * (1 + 1 / re_fade_in_speed); + } + + protected override Color4 ColorAtTime(double pointTime) + { + var color = Color4.White; + + double timeDoingInitialFadeOut = initialFadeOutTime - pointTime; + + if (timeDoingInitialFadeOut > 0) + { + float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1); + fraction = MathF.Pow(fraction, 5); + color.A = (1 - fraction) * initial_alpha; + } + + if (color.A > 0) + { + double timeDoingReFadeIn = reFadeInTime - pointTime; + double timeDoingFinalFadeOut = finalFadeOutTime - pointTime; + + if (timeDoingFinalFadeOut > 0) + { + float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1); + fraction = MathF.Pow(fraction, 5); + color.A = (1 - fraction) * re_fade_in_alpha; + } + else if (timeDoingReFadeIn > 0) + { + float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1); + fraction = 1 - MathF.Pow(1 - fraction, 5); + color.A = fraction * (re_fade_in_alpha - color.A) + color.A; + } + } + + return color; + } + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 6c60392c94..52de537098 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -1,33 +1,175 @@ // 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.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Rendering.Vertices; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Framework.Timing; +using osu.Framework.Utils; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning { - public abstract class Smoke : TexturedPath + public abstract class Smoke : Drawable, ITexturedShaderDrawable { - protected bool IsActive { get; private set; } - protected double SmokeTimeStart { get; private set; } = double.MinValue; - protected double SmokeTimeEnd { get; private set; } = double.MinValue; + public IShader? TextureShader { get; private set; } + public IShader? RoundedTextureShader { get; private set; } - protected readonly List SmokeVertexPositions = new List(); - protected readonly List SmokeVertexTimes = new List(); + private float radius = 1; + protected float Radius + { + get => radius; + set + { + if (radius == value) + return; + + radius = value; + Invalidate(Invalidation.DrawNode); + } + } + + + private int rotationSeed = RNG.Next(); + protected int RotationSeed + { + get => rotationSeed; + set + { + if (rotationSeed == value) + return; + + rotationSeed = value; + Invalidate(Invalidation.DrawNode); + } + } + + private Texture? texture; + protected Texture? Texture + { + get => texture; + set + { + texture = value; + Invalidate(Invalidation.DrawNode); + } + } + + private double smokeTimeStart = double.MinValue; + protected double SmokeStartTime + { + get => smokeTimeStart; + private set + { + if (smokeTimeStart == value) + return; + + smokeTimeStart = value; + Invalidate(Invalidation.DrawNode); + } + } + + private double smokeTimeEnd = double.MaxValue; + protected double SmokeEndTime + { + get => smokeTimeEnd; + private set + { + if (smokeTimeEnd == value) + return; + + smokeTimeEnd = value; + Invalidate(Invalidation.DrawNode); + } + } + + public override IFrameBasedClock Clock + { + get => base.Clock; + set + { + base.Clock = value; + Invalidate(Invalidation.DrawNode); + } + } + + private Vector2 topLeft; + protected Vector2 TopLeft + { + get => topLeft; + set + { + if (topLeft == value) + return; + + topLeft = value; + Invalidate(Invalidation.All); + } + } + + private Vector2 bottomRight; + protected Vector2 BottomRight + { + get => bottomRight; + set + { + if (bottomRight == value) + return; + + bottomRight = value; + Invalidate(Invalidation.Layout); + } + } + + protected abstract double LifetimeAfterSmokeEnd { get; } + protected virtual float PointInterval => Radius * 7f / 8; + protected bool IsActive { get; private set; } + + protected readonly List SmokePoints = new List(); + + private float totalDistance; + private Vector2? lastPosition = null; + + private const double max_duration = 60_000; + + public override float Height + { + get => base.Height = BottomRight.Y - TopLeft.Y; + set => throw new InvalidOperationException($"Cannot manually set {nameof(Height)} of {nameof(Smoke)}."); + } + + public override float Width + { + get => base.Width = BottomRight.X - TopLeft.X; + set => throw new InvalidOperationException($"Cannot manually set {nameof(Width)} of {nameof(Smoke)}."); + } + + public override Vector2 Size + { + get => base.Size = BottomRight - TopLeft; + set => throw new InvalidOperationException($"Cannot manually set {nameof(Size)} of {nameof(Smoke)}."); + } [Resolved(CanBeNull = true)] private SmokeContainer? smokeContainer { get; set; } - protected struct SmokePoint + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) { - public Vector2 Position; - public double Time; + RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE); } protected override void LoadComplete() @@ -36,72 +178,107 @@ namespace osu.Game.Rulesets.Osu.Skinning if (smokeContainer != null) { - smokeContainer.SmokeMoved += guardOnSmokeMoved; - smokeContainer.SmokeEnded += guardOnSmokeEnded; + smokeContainer.SmokeMoved += onSmokeMoved; + smokeContainer.SmokeEnded += onSmokeEnded; IsActive = true; } Anchor = Anchor.TopLeft; Origin = Anchor.TopLeft; - SmokeTimeStart = Time.Current; + SmokeStartTime = Time.Current; } - private void guardOnSmokeMoved(Vector2 position, double time) + private void onSmokeMoved(Vector2 position, double time) { - if (IsActive) - OnSmokeMoved(position, time); - } + if (!IsActive) + return; - private void guardOnSmokeEnded(double time) - { - if (IsActive) - OnSmokeEnded(time); - } + lastPosition ??= position; - protected virtual void OnSmokeMoved(Vector2 position, double time) - { - addSmokeVertex(position, time); - } + float delta = (position - (Vector2)lastPosition).LengthFast; + totalDistance += delta; + int count = (int)(totalDistance / PointInterval); - private void addSmokeVertex(Vector2 position, double time) - { - Debug.Assert(SmokeVertexTimes.Count == SmokeVertexPositions.Count); - - if (SmokeVertexTimes.Count > 0 && SmokeVertexTimes.Last() > time) + if (count > 0) { - int index = ~SmokeVertexTimes.BinarySearch(time, new UpperBoundComparer()); + Vector2 increment = position - (Vector2)lastPosition; + increment.NormalizeFast(); - SmokeVertexTimes.RemoveRange(index, SmokeVertexTimes.Count - index); - SmokeVertexPositions.RemoveRange(index, SmokeVertexPositions.Count - index); + Vector2 pointPos = (PointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition; + increment *= PointInterval; + + if (SmokePoints.Count > 0 && SmokePoints[^1].Time > time) + { + int index = ~SmokePoints.BinarySearch(new SmokePoint { Time = time }, new SmokePoint.UpperBoundComparer()); + SmokePoints.RemoveRange(index, SmokePoints.Count - index); + recalculateBounds(); + } + + totalDistance %= PointInterval; + for (int i = 0; i < count; i++) + { + SmokePoints.Add(new SmokePoint + { + Position = pointPos, + Time = time, + }); + + pointPos += increment; + } + + Invalidate(Invalidation.DrawNode); + adaptBounds(position); } - SmokeVertexTimes.Add(time); - SmokeVertexPositions.Add(position); + lastPosition = position; + + if (time - SmokeStartTime > max_duration) + onSmokeEnded(time); } - protected virtual void OnSmokeEnded(double time) + private void recalculateBounds() { - IsActive = false; - SmokeTimeEnd = time; + TopLeft = BottomRight = Vector2.Zero; + + foreach (var point in SmokePoints) + adaptBounds(point.Position); } + private void adaptBounds(Vector2 position) + { + if (position.X < TopLeft.X) + TopLeft = new Vector2(position.X, TopLeft.Y); + else if (position.X > BottomRight.X) + BottomRight = new Vector2(position.X, BottomRight.Y); + + if (position.Y < TopLeft.Y) + TopLeft = new Vector2(TopLeft.X, position.Y); + else if (position.Y > BottomRight.Y) + BottomRight = new Vector2(BottomRight.X, position.Y); + } + + private void onSmokeEnded(double time) + { + if (!IsActive) + return; + + IsActive = false; + SmokeEndTime = time; + LifetimeEnd = time + LifetimeAfterSmokeEnd + 100; + + // TODO: HYPER MEGA JANK WTF?? + if (Parent is SkinnableDrawable) + Parent.LifetimeEnd = LifetimeEnd; + } + + protected abstract override DrawNode CreateDrawNode(); + protected override void Update() { base.Update(); - const double visible_duration = 8000; - const float disappear_speed = 3; - - int index = 0; - if (!IsActive) - { - double cutoffTime = SmokeTimeStart + disappear_speed * (Time.Current - (SmokeTimeEnd + visible_duration)); - index = ~SmokeVertexTimes.BinarySearch(cutoffTime, new UpperBoundComparer()); - } - Vertices = new List(SmokeVertexPositions.Skip(index)); - - Position = -PositionInBoundingBox(Vector2.Zero); + Position = TopLeft; } protected override void Dispose(bool isDisposing) @@ -110,21 +287,163 @@ namespace osu.Game.Rulesets.Osu.Skinning if (smokeContainer != null) { - smokeContainer.SmokeMoved -= guardOnSmokeMoved; - smokeContainer.SmokeEnded -= guardOnSmokeEnded; + smokeContainer.SmokeMoved -= onSmokeMoved; + smokeContainer.SmokeEnded -= onSmokeEnded; } } - private struct UpperBoundComparer : IComparer + protected struct SmokePoint { - public int Compare(double x, double target) - { - // By returning -1 when the target value is equal to x, guarantees that the - // element at BinarySearch's returned index will always be the first element - // larger. Since 0 is never returned, the target is never "found", so the return - // value will be the index's complement. + public Vector2 Position; + public double Time; - return x > target ? 1 : -1; + public struct UpperBoundComparer : IComparer + { + public int Compare(SmokePoint x, SmokePoint target) + { + // By returning -1 when the target value is equal to x, guarantees that the + // element at BinarySearch's returned index will always be the first element + // larger. Since 0 is never returned, the target is never "found", so the return + // value will be the index's complement. + + return x.Time > target.Time ? 1 : -1; + } + } + } + + protected abstract class SmokeDrawNode : TexturedShaderDrawNode + { + protected new Smoke Source => (Smoke)base.Source; + + protected IVertexBatch? QuadBatch; + + protected readonly List Points = new List(); + + protected float Radius; + protected Vector2 DrawSize; + protected Vector2 PositionOffset; + protected Texture? Texture; + + protected double SmokeStartTime; + protected double SmokeEndTime; + protected double CurrentTime; + + protected RectangleF TextureRect; + + private IFrameBasedClock? clock; + + private int rotationSeed; + private Random rotationRNG = new Random(); + + public SmokeDrawNode(ITexturedShaderDrawable source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + Points.Clear(); + Points.AddRange(Source.SmokePoints); + + Radius = Source.Radius; + DrawSize = Source.DrawSize; + PositionOffset = Source.TopLeft; + Texture = Source.Texture; + clock = Source.Clock; + + SmokeStartTime = Source.SmokeStartTime; + SmokeEndTime = Source.SmokeEndTime; + + rotationSeed = Source.RotationSeed; + } + + public sealed override void Draw(IRenderer renderer) + { + base.Draw(renderer); + + if (Points.Count == 0) + return; + + QuadBatch ??= renderer.CreateQuadBatch(7200, 10); + Texture ??= renderer.WhitePixel; + + var shader = GetAppropriateShader(renderer); + shader.Bind(); + Texture.Bind(); + + UpdateDrawVariables(renderer); + UpdateVertexBuffer(); + + shader.Unbind(); + } + + protected Color4 ColorAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour + ? ((SRGBColour)DrawColourInfo.Colour).Linear + : DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, DrawSize)).Linear; + + protected abstract Color4 ColorAtTime(double pointTime); + + protected virtual void UpdateDrawVariables(IRenderer renderer) + { + Debug.Assert(clock != null); + Debug.Assert(Texture != null); + + CurrentTime = clock.CurrentTime; + TextureRect = Texture.GetTextureRect(); + rotationRNG = new Random(rotationSeed); + } + + protected virtual void UpdateVertexBuffer() + { + foreach (var point in Points) + drawPointQuad(point); + } + + private Vector2 nextTextureDirection() + { + float angle = (float)rotationRNG.NextDouble() * 2 * MathF.PI; + return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); + } + + private void drawPointQuad(SmokePoint point) + { + Debug.Assert(QuadBatch != null); + + var color = ColorAtTime(point.Time); + var dir = nextTextureDirection(); + var ortho = dir.PerpendicularLeft; + + var localTopLeft = point.Position + (Radius * (-ortho - dir)) - PositionOffset; + var localTopRight = point.Position + (Radius * (-ortho + dir)) - PositionOffset; + var localBotLeft = point.Position + (Radius * (ortho - dir)) - PositionOffset; + var localBotRight = point.Position + (Radius * (ortho + dir)) - PositionOffset; + + QuadBatch.Add(new TexturedVertex2D + { + Position = Vector2Extensions.Transform(localTopLeft, DrawInfo.Matrix), + TexturePosition = TextureRect.TopLeft, + Colour = Color4Extensions.Multiply(ColorAtPosition(localTopLeft), color), + }); + QuadBatch.Add(new TexturedVertex2D + { + Position = Vector2Extensions.Transform(localTopRight, DrawInfo.Matrix), + TexturePosition = TextureRect.TopRight, + Colour = Color4Extensions.Multiply(ColorAtPosition(localTopRight), color), + }); + QuadBatch.Add(new TexturedVertex2D + { + Position = Vector2Extensions.Transform(localBotRight, DrawInfo.Matrix), + TexturePosition = TextureRect.BottomRight, + Colour = Color4Extensions.Multiply(ColorAtPosition(localBotRight), color), + }); + QuadBatch.Add(new TexturedVertex2D + { + Position = Vector2Extensions.Transform(localBotLeft, DrawInfo.Matrix), + TexturePosition = TextureRect.BottomLeft, + Colour = Color4Extensions.Multiply(ColorAtPosition(localBotLeft), color), + }); } } } diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index 865204f8cc..b8897f2cf1 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; From 06178104c8953a059f5f26b3b04a5e8978ed551b Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 17:55:06 -0700 Subject: [PATCH 009/295] Show smoke in replays --- osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs | 3 +++ osu.Game/Replays/Legacy/LegacyReplayFrame.cs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs index 85060261fe..8082c5aef4 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Osu.Replays Position = currentFrame.Position; if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton); + if (currentFrame.Smoke) Actions.Add(OsuAction.Smoke); } public LegacyReplayFrame ToLegacy(IBeatmap beatmap) @@ -41,6 +42,8 @@ namespace osu.Game.Rulesets.Osu.Replays state |= ReplayButtonState.Left1; if (Actions.Contains(OsuAction.RightButton)) state |= ReplayButtonState.Right1; + if (Actions.Contains(OsuAction.Smoke)) + state |= ReplayButtonState.Smoke; return new LegacyReplayFrame(Time, Position.X, Position.Y, state); } diff --git a/osu.Game/Replays/Legacy/LegacyReplayFrame.cs b/osu.Game/Replays/Legacy/LegacyReplayFrame.cs index f6abf259e8..f345504ca1 100644 --- a/osu.Game/Replays/Legacy/LegacyReplayFrame.cs +++ b/osu.Game/Replays/Legacy/LegacyReplayFrame.cs @@ -46,6 +46,10 @@ namespace osu.Game.Replays.Legacy [IgnoreMember] public bool MouseRight2 => ButtonState.HasFlagFast(ReplayButtonState.Right2); + [JsonIgnore] + [IgnoreMember] + public bool Smoke => ButtonState.HasFlagFast(ReplayButtonState.Smoke); + [Key(3)] public ReplayButtonState ButtonState; From 0138663bdcb3d534dd892a68f545f310eb2dfe4d Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 18:32:33 -0700 Subject: [PATCH 010/295] Fix InspectCode errors --- .../Skinning/Default/DefaultSmoke.cs | 2 +- .../Skinning/Legacy/LegacySmoke.cs | 3 +-- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 15 +++++++++++---- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 +-- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index ecba9d4904..e7f526da31 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; using osuTK.Graphics; @@ -47,6 +46,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default color.A = alpha; double timeDoingFadeOut = fadeOutTime - pointTime; + if (timeDoingFadeOut > 0) { float fraction = Math.Clamp((float)(1 - (timeDoingFadeOut / fade_out_duration)), 0, 1); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index 6c2c80f746..23ab2d186f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; using osu.Game.Skinning; @@ -31,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } } - private ISkin skin; + private readonly ISkin skin; public LegacySmoke(ISkin skin) { diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 52de537098..382e54e2ab 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Osu.Skinning public IShader? RoundedTextureShader { get; private set; } private float radius = 1; + protected float Radius { get => radius; @@ -41,8 +42,8 @@ namespace osu.Game.Rulesets.Osu.Skinning } } - private int rotationSeed = RNG.Next(); + protected int RotationSeed { get => rotationSeed; @@ -57,6 +58,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } private Texture? texture; + protected Texture? Texture { get => texture; @@ -68,6 +70,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } private double smokeTimeStart = double.MinValue; + protected double SmokeStartTime { get => smokeTimeStart; @@ -82,6 +85,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } private double smokeTimeEnd = double.MaxValue; + protected double SmokeEndTime { get => smokeTimeEnd; @@ -106,6 +110,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } private Vector2 topLeft; + protected Vector2 TopLeft { get => topLeft; @@ -115,11 +120,12 @@ namespace osu.Game.Rulesets.Osu.Skinning return; topLeft = value; - Invalidate(Invalidation.All); + Invalidate(); } } private Vector2 bottomRight; + protected Vector2 BottomRight { get => bottomRight; @@ -140,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Skinning protected readonly List SmokePoints = new List(); private float totalDistance; - private Vector2? lastPosition = null; + private Vector2? lastPosition; private const double max_duration = 60_000; @@ -216,6 +222,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } totalDistance %= PointInterval; + for (int i = 0; i < count; i++) { SmokePoints.Add(new SmokePoint @@ -335,7 +342,7 @@ namespace osu.Game.Rulesets.Osu.Skinning private int rotationSeed; private Random rotationRNG = new Random(); - public SmokeDrawNode(ITexturedShaderDrawable source) + protected SmokeDrawNode(ITexturedShaderDrawable source) : base(source) { } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 8085c07912..2e67e91460 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -32,7 +32,6 @@ namespace osu.Game.Rulesets.Osu.UI public class OsuPlayfield : Playfield { private readonly PlayfieldBorder playfieldBorder; - private readonly SmokeContainer smokeContainer; private readonly ProxyContainer approachCircles; private readonly ProxyContainer spinnerProxies; private readonly JudgementContainer judgementLayer; @@ -55,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.UI InternalChildren = new Drawable[] { playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both }, - smokeContainer = new SmokeContainer { RelativeSizeAxes = Axes.Both }, + new SmokeContainer { RelativeSizeAxes = Axes.Both }, spinnerProxies = new ProxyContainer { RelativeSizeAxes = Axes.Both }, FollowPoints = new FollowPointRenderer { RelativeSizeAxes = Axes.Both }, judgementLayer = new JudgementContainer { RelativeSizeAxes = Axes.Both }, diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index b8897f2cf1..07a073c3e5 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.UI public event Action? SmokeMoved; public event Action? SmokeEnded; - private bool isSmoking = false; + private bool isSmoking; public override bool ReceivePositionalInputAt(Vector2 _) => true; From 6852577dad463994280fbd8a171f4f6098b1bdd1 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 19:08:01 -0700 Subject: [PATCH 011/295] Remove smoke from key overlay --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 15 +++++++++++++++ osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index dec965e567..6500ade7ea 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -10,6 +10,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges.Events; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Osu { @@ -56,6 +57,20 @@ namespace osu.Game.Rulesets.Osu return base.HandleMouseTouchStateChange(e); } + public override void Attach(KeyCounterDisplay keyCounter) + { + var receptor = new ActionReceptor(keyCounter); + + KeyBindingContainer.Add(receptor); + + keyCounter.SetReceptor(receptor); + keyCounter.AddRange(new[] + { + new KeyCounterAction(OsuAction.LeftButton), + new KeyCounterAction(OsuAction.RightButton), + }); + } + private class OsuKeyBindingContainer : RulesetKeyBindingContainer { public bool AllowUserPresses = true; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 7c37913576..902579d9d2 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.UI #region Key Counter Attachment - public void Attach(KeyCounterDisplay keyCounter) + public virtual void Attach(KeyCounterDisplay keyCounter) { var receptor = new ActionReceptor(keyCounter); From a0e31018a18af7eeec7c4608cd5dcce7387d0f99 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 22:06:07 -0700 Subject: [PATCH 012/295] Copy stable smoke's fade/alpha values, blending, scale, and rotation --- .../Skinning/Default/DefaultSmoke.cs | 15 +++- .../Skinning/Legacy/LegacySmoke.cs | 83 ++++++++++++++++--- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 57 +++++-------- 3 files changed, 108 insertions(+), 47 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index e7f526da31..0ba0143466 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; +using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Default @@ -40,12 +41,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default fadeOutTime = SmokeStartTime + fade_out_speed * (CurrentTime - (SmokeEndTime + fade_out_delay)); } - protected override Color4 ColorAtTime(double pointTime) + protected override Color4 PointColor(SmokePoint point) { var color = Color4.White; color.A = alpha; - double timeDoingFadeOut = fadeOutTime - pointTime; + double timeDoingFadeOut = fadeOutTime - point.Time; if (timeDoingFadeOut > 0) { @@ -56,6 +57,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default return color; } + + protected override float PointScale(SmokePoint point) + { + throw new NotImplementedException(); + } + + protected override Vector2 PointDirection(SmokePoint point) + { + throw new NotImplementedException(); + } } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index 23ab2d186f..093d8f87eb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -4,29 +4,59 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Rendering; +using osu.Framework.Utils; using osu.Game.Skinning; +using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacySmoke : Smoke { - private const double initial_fade_out_duration = 2500; + // fade values + private const double initial_fade_out_duration = 4000; private const double re_fade_in_speed = 3; private const double re_fade_in_duration = 50; - private const double final_fade_out_duration = 7500; + private const double final_fade_out_speed = 2; + private const double final_fade_out_duration = 8000; - private const float initial_alpha = 0.8f; - private const float re_fade_in_alpha = 1.4f; + private const float initial_alpha = 0.6f; + private const float re_fade_in_alpha = 1f; + + // scale values + private const double scale_duration = 1200; + + private const float initial_scale = 0.65f; + private const float final_scale = 1f; + + // rotation values + private const double rotation_duration = 500; + + private const float max_rotation = 0.25f; + + private int rotationSeed = RNG.Next(); + + protected int RotationSeed + { + get => rotationSeed; + set + { + if (rotationSeed == value) + return; + + rotationSeed = value; + Invalidate(Invalidation.DrawNode); + } + } protected override double LifetimeAfterSmokeEnd { get { double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); - return final_fade_out_duration + initialFadeOutDurationTrunc * (1 + re_fade_in_speed); + return final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; } } @@ -49,11 +79,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected class LegacySmokeDrawNode : SmokeDrawNode { + protected new LegacySmoke Source => (LegacySmoke)base.Source; + private double initialFadeOutDurationTrunc; private double initialFadeOutTime; private double reFadeInTime; private double finalFadeOutTime; + private int rotationSeed; + private Random rotationRNG = new Random(); + public LegacySmokeDrawNode(ITexturedShaderDrawable source) : base(source) { @@ -64,34 +99,35 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy base.ApplyState(); initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); + rotationSeed = Source.RotationSeed; } protected override void UpdateDrawVariables(IRenderer renderer) { base.UpdateDrawVariables(renderer); + rotationRNG = new Random(rotationSeed); initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime); reFadeInTime = re_fade_in_speed * (CurrentTime - SmokeEndTime) + SmokeEndTime - initialFadeOutDurationTrunc; - finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc * (1 + 1 / re_fade_in_speed); + finalFadeOutTime = final_fade_out_speed * (CurrentTime - SmokeEndTime) + SmokeEndTime - initialFadeOutDurationTrunc * (1 + 1 / re_fade_in_speed); } - protected override Color4 ColorAtTime(double pointTime) + protected override Color4 PointColor(SmokePoint point) { var color = Color4.White; - double timeDoingInitialFadeOut = initialFadeOutTime - pointTime; + double timeDoingInitialFadeOut = initialFadeOutTime - point.Time; if (timeDoingInitialFadeOut > 0) { float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1); - fraction = MathF.Pow(fraction, 5); color.A = (1 - fraction) * initial_alpha; } if (color.A > 0) { - double timeDoingReFadeIn = reFadeInTime - pointTime; - double timeDoingFinalFadeOut = finalFadeOutTime - pointTime; + double timeDoingReFadeIn = reFadeInTime - point.Time; + double timeDoingFinalFadeOut = finalFadeOutTime - point.Time; if (timeDoingFinalFadeOut > 0) { @@ -109,6 +145,31 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return color; } + + protected override float PointScale(SmokePoint point) + { + double timeDoingScale = CurrentTime - point.Time; + float fraction = Math.Clamp((float)(timeDoingScale / scale_duration), 0, 1); + fraction = 1 - MathF.Pow(1 - fraction, 5); + return fraction * (final_scale - initial_scale) + initial_scale; + } + + protected override Vector2 PointDirection(SmokePoint point) + { + float initialAngle = MathF.Atan2(point.Direction.Y, point.Direction.X); + float finalAngle = initialAngle + nextRotation(); + + double timeDoingRotation = CurrentTime - point.Time; + float fraction = Math.Clamp((float)(timeDoingRotation / rotation_duration), 0, 1); + fraction = 1 - MathF.Pow(1 - fraction, 5); + float angle = fraction * (finalAngle - initialAngle) + initialAngle; + + return toVector2(angle); + } + + private float nextRotation() => max_rotation * ((float)rotationRNG.NextDouble() * 2 - 1); + + private Vector2 toVector2(float angle) => new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 382e54e2ab..b725cc028b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -42,21 +42,6 @@ namespace osu.Game.Rulesets.Osu.Skinning } } - private int rotationSeed = RNG.Next(); - - protected int RotationSeed - { - get => rotationSeed; - set - { - if (rotationSeed == value) - return; - - rotationSeed = value; - Invalidate(Invalidation.DrawNode); - } - } - private Texture? texture; protected Texture? Texture @@ -195,6 +180,12 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokeStartTime = Time.Current; } + private Vector2 nextPointDirection() + { + float angle = RNG.NextSingle(0, 2 * MathF.PI); + return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); + } + private void onSmokeMoved(Vector2 position, double time) { if (!IsActive) @@ -229,6 +220,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { Position = pointPos, Time = time, + Direction = nextPointDirection(), }); pointPos += increment; @@ -303,6 +295,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { public Vector2 Position; public double Time; + public Vector2 Direction; public struct UpperBoundComparer : IComparer { @@ -339,9 +332,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private IFrameBasedClock? clock; - private int rotationSeed; - private Random rotationRNG = new Random(); - protected SmokeDrawNode(ITexturedShaderDrawable source) : base(source) { @@ -362,8 +352,6 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokeStartTime = Source.SmokeStartTime; SmokeEndTime = Source.SmokeEndTime; - - rotationSeed = Source.RotationSeed; } public sealed override void Draw(IRenderer renderer) @@ -377,6 +365,9 @@ namespace osu.Game.Rulesets.Osu.Skinning Texture ??= renderer.WhitePixel; var shader = GetAppropriateShader(renderer); + + renderer.SetBlend(BlendingParameters.Additive); + shader.Bind(); Texture.Bind(); @@ -390,7 +381,11 @@ namespace osu.Game.Rulesets.Osu.Skinning ? ((SRGBColour)DrawColourInfo.Colour).Linear : DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, DrawSize)).Linear; - protected abstract Color4 ColorAtTime(double pointTime); + protected abstract Color4 PointColor(SmokePoint point); + + protected abstract float PointScale(SmokePoint point); + + protected abstract Vector2 PointDirection(SmokePoint point); protected virtual void UpdateDrawVariables(IRenderer renderer) { @@ -399,7 +394,6 @@ namespace osu.Game.Rulesets.Osu.Skinning CurrentTime = clock.CurrentTime; TextureRect = Texture.GetTextureRect(); - rotationRNG = new Random(rotationSeed); } protected virtual void UpdateVertexBuffer() @@ -408,24 +402,19 @@ namespace osu.Game.Rulesets.Osu.Skinning drawPointQuad(point); } - private Vector2 nextTextureDirection() - { - float angle = (float)rotationRNG.NextDouble() * 2 * MathF.PI; - return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); - } - private void drawPointQuad(SmokePoint point) { Debug.Assert(QuadBatch != null); - var color = ColorAtTime(point.Time); - var dir = nextTextureDirection(); + var color = PointColor(point); + float scale = PointScale(point); + var dir = PointDirection(point); var ortho = dir.PerpendicularLeft; - var localTopLeft = point.Position + (Radius * (-ortho - dir)) - PositionOffset; - var localTopRight = point.Position + (Radius * (-ortho + dir)) - PositionOffset; - var localBotLeft = point.Position + (Radius * (ortho - dir)) - PositionOffset; - var localBotRight = point.Position + (Radius * (ortho + dir)) - PositionOffset; + var localTopLeft = point.Position + (Radius * scale * (-ortho - dir)) - PositionOffset; + var localTopRight = point.Position + (Radius * scale * (-ortho + dir)) - PositionOffset; + var localBotLeft = point.Position + (Radius * scale * (ortho - dir)) - PositionOffset; + var localBotRight = point.Position + (Radius * scale * (ortho + dir)) - PositionOffset; QuadBatch.Add(new TexturedVertex2D { From 8474335aeaf520b61a82b3800f473728e77b402d Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 22:08:45 -0700 Subject: [PATCH 013/295] Remove hacky LifetimeEnd workaround --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index b725cc028b..822dc113f1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -16,7 +16,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Rulesets.Osu.UI; -using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -265,10 +264,6 @@ namespace osu.Game.Rulesets.Osu.Skinning IsActive = false; SmokeEndTime = time; LifetimeEnd = time + LifetimeAfterSmokeEnd + 100; - - // TODO: HYPER MEGA JANK WTF?? - if (Parent is SkinnableDrawable) - Parent.LifetimeEnd = LifetimeEnd; } protected abstract override DrawNode CreateDrawNode(); From 3eb28881e4b0052894f9b60c0ebe649e02921118 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Sun, 18 Sep 2022 22:14:54 -0700 Subject: [PATCH 014/295] Temp default smoke scale/rotation anims --- osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index 0ba0143466..6eebd18305 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -58,15 +58,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default return color; } - protected override float PointScale(SmokePoint point) - { - throw new NotImplementedException(); - } + protected override float PointScale(SmokePoint point) => 1f; - protected override Vector2 PointDirection(SmokePoint point) - { - throw new NotImplementedException(); - } + protected override Vector2 PointDirection(SmokePoint point) => point.Direction; } } } From 8204090e471f6784c9d1a520ac97b61e7391ff66 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 19 Sep 2022 00:07:22 -0700 Subject: [PATCH 015/295] Scale smoke radius based on texture width --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs | 5 +---- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index 093d8f87eb..d8c3ff3521 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -65,7 +65,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public LegacySmoke(ISkin skin) { this.skin = skin; - Radius = 3; } protected override void LoadComplete() @@ -164,12 +163,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy fraction = 1 - MathF.Pow(1 - fraction, 5); float angle = fraction * (finalAngle - initialAngle) + initialAngle; - return toVector2(angle); + return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); } private float nextRotation() => max_rotation * ((float)rotationRNG.NextDouble() * 2 - 1); - - private Vector2 toVector2(float angle) => new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 822dc113f1..bada67816b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -26,11 +26,11 @@ namespace osu.Game.Rulesets.Osu.Skinning public IShader? TextureShader { get; private set; } public IShader? RoundedTextureShader { get; private set; } - private float radius = 1; + private float? radius; protected float Radius { - get => radius; + get => radius ?? Texture?.DisplayWidth * 0.165f ?? 3; set { if (radius == value) From 03cc6b8af3ef954b46c9ba467bc60e4c8a9c4785 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Sep 2022 23:44:03 +0900 Subject: [PATCH 016/295] Fix parameter name --- osu.Game/Localisation/GeneralSettingsStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index 8506971756..3278b20983 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -67,7 +67,7 @@ namespace osu.Game.Localisation /// /// "You are running the latest release ({0})" /// - public static LocalisableString RunningLatestRelease(string arg0) => new TranslatableString(getKey(@"running_latest_release"), @"You are running the latest release ({0})", arg0); + public static LocalisableString RunningLatestRelease(string version) => new TranslatableString(getKey(@"running_latest_release"), @"You are running the latest release ({0})", version); private static string getKey(string key) => $"{prefix}:{key}"; } From 74056201e7903fd572bd1a4a9f580cd74db2c78a Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 19 Sep 2022 09:15:59 -0700 Subject: [PATCH 017/295] Return smoke key to key overlay --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 14 -------------- osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 6500ade7ea..9332aa679a 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -57,20 +57,6 @@ namespace osu.Game.Rulesets.Osu return base.HandleMouseTouchStateChange(e); } - public override void Attach(KeyCounterDisplay keyCounter) - { - var receptor = new ActionReceptor(keyCounter); - - KeyBindingContainer.Add(receptor); - - keyCounter.SetReceptor(receptor); - keyCounter.AddRange(new[] - { - new KeyCounterAction(OsuAction.LeftButton), - new KeyCounterAction(OsuAction.RightButton), - }); - } - private class OsuKeyBindingContainer : RulesetKeyBindingContainer { public bool AllowUserPresses = true; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index ce87d8b6c0..1a97153f2f 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.UI #region Key Counter Attachment - public virtual void Attach(KeyCounterDisplay keyCounter) + public void Attach(KeyCounterDisplay keyCounter) { var receptor = new ActionReceptor(keyCounter); From c3b8e1d718a8e5c3bc259a752d39aa632a2359dc Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 19 Sep 2022 10:16:05 -0700 Subject: [PATCH 018/295] Fix test and spawn smoke immediately --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 1 - osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 14 +++++++++----- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 4 ++++ .../Visual/Gameplay/TestSceneGameplayRewinding.cs | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 9332aa679a..dec965e567 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -10,7 +10,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges.Events; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Osu { diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index bada67816b..7359f6029e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -166,17 +166,21 @@ namespace osu.Game.Rulesets.Osu.Skinning { base.LoadComplete(); + Anchor = Anchor.TopLeft; + Origin = Anchor.TopLeft; + + SmokeStartTime = Time.Current; + + totalDistance = PointInterval; + if (smokeContainer != null) { smokeContainer.SmokeMoved += onSmokeMoved; smokeContainer.SmokeEnded += onSmokeEnded; IsActive = true; + + onSmokeMoved(smokeContainer.LastMousePosition, Time.Current); } - - Anchor = Anchor.TopLeft; - Origin = Anchor.TopLeft; - - SmokeStartTime = Time.Current; } private Vector2 nextPointDirection() diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index 07a073c3e5..2b5bddf959 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Osu.UI public event Action? SmokeMoved; public event Action? SmokeEnded; + public Vector2 LastMousePosition; + private bool isSmoking; public override bool ReceivePositionalInputAt(Vector2 _) => true; @@ -50,6 +52,8 @@ namespace osu.Game.Rulesets.Osu.UI if (isSmoking) SmokeMoved?.Invoke(e.MousePosition, Time.Current); + LastMousePosition = e.MousePosition; + return base.OnMouseMove(e); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 0a32513834..bd55ed8bd6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); addSeekStep(3000); AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); - AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7)); + AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Select(kc => kc.CountPresses).Sum() == 15); AddStep("clear results", () => Player.Results.Clear()); addSeekStep(0); AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); From d22d009fb33a97df29dc1b9759c3c625a95259c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A2=E3=82=BA=E3=82=BF=E3=82=B1?= Date: Tue, 20 Sep 2022 14:02:11 +0900 Subject: [PATCH 019/295] fix review points. items not included in this localization were reverted. --- .../MaintenanceSettingsStrings.cs | 30 ------------------- .../MassDeleteConfirmationDialog.cs | 3 +- .../MassVideoDeleteConfirmationDialog.cs | 3 +- .../StableDirectoryLocationDialog.cs | 9 +++--- .../StableDirectorySelectScreen.cs | 3 +- 5 files changed, 7 insertions(+), 41 deletions(-) diff --git a/osu.Game/Localisation/MaintenanceSettingsStrings.cs b/osu.Game/Localisation/MaintenanceSettingsStrings.cs index 4648682e64..8aa0adf7a0 100644 --- a/osu.Game/Localisation/MaintenanceSettingsStrings.cs +++ b/osu.Game/Localisation/MaintenanceSettingsStrings.cs @@ -134,36 +134,6 @@ namespace osu.Game.Localisation /// public static LocalisableString RestoredAllDeletedModPresets => new TranslatableString(getKey(@"restored_all_deleted_mod_presets"), @"Restored all deleted mod presets!"); - /// - /// "Everything?" - /// - public static LocalisableString MassDeleteConfirmation => new TranslatableString(getKey(@"mass_delete_confirmation"), @"Everything?"); - - /// - /// "All beatmap videos? This cannot be undone!" - /// - public static LocalisableString MassVideoDeleteConfirmation => new TranslatableString(getKey(@"mass_video_delete_confirmation"), @"All beatmap videos? This cannot be undone!"); - - /// - /// "Failed to automatically locate an osu!stable installation." - /// - public static LocalisableString StableDirectoryLocationHeader => new TranslatableString(getKey(@"stable_directory_location_header"), @"Failed to automatically locate an osu!stable installation."); - - /// - /// "An existing install could not be located. If you know where it is, you can help locate it." - /// - public static LocalisableString StableDirectoryLocationBody => new TranslatableString(getKey(@"stable_directory_location_body"), @"An existing install could not be located. If you know where it is, you can help locate it."); - - /// - /// "Sure! I know where it is located!" - /// - public static LocalisableString StableDirectoryLocationOk => new TranslatableString(getKey(@"stable_directory_location_ok"), @"Sure! I know where it is located!"); - - /// - /// "Actually I don't have osu!stable installed." - /// - public static LocalisableString StableDirectoryLocationCancel => new TranslatableString(getKey(@"stable_directory_location_cancel"), @"Actually I don't have osu!stable installed."); - /// /// "Please select your osu!stable install location" /// diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs index bcfccaa5e2..19e6f83dac 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Localisation; using osu.Game.Overlays.Dialog; namespace osu.Game.Overlays.Settings.Sections.Maintenance @@ -11,7 +10,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public MassDeleteConfirmationDialog(Action deleteAction) { - BodyText = MaintenanceSettingsStrings.MassDeleteConfirmation; + BodyText = "Everything?"; DeleteAction = deleteAction; } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs index a386c64806..fc8c9d497b 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -11,7 +10,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public MassVideoDeleteConfirmationDialog(Action deleteAction) : base(deleteAction) { - BodyText = MaintenanceSettingsStrings.MassVideoDeleteConfirmation; + BodyText = "All beatmap videos? This cannot be undone!"; } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 7b7ea7cee0..8aff4520b5 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; -using osu.Game.Localisation; using osu.Game.Overlays.Dialog; using osu.Game.Screens; @@ -20,20 +19,20 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { - HeaderText = MaintenanceSettingsStrings.StableDirectoryLocationHeader; - BodyText = MaintenanceSettingsStrings.StableDirectoryLocationBody; + HeaderText = "Failed to automatically locate an osu!stable installation."; + BodyText = "An existing install could not be located. If you know where it is, you can help locate it."; Icon = FontAwesome.Solid.QuestionCircle; Buttons = new PopupDialogButton[] { new PopupDialogOkButton { - Text = MaintenanceSettingsStrings.StableDirectoryLocationOk, + Text = "Sure! I know where it is located!", Action = () => Schedule(() => performer.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) }, new PopupDialogCancelButton { - Text = MaintenanceSettingsStrings.StableDirectoryLocationCancel, + Text = "Actually I don't have osu!stable installed.", Action = () => taskCompletionSource.TrySetCanceled() } }; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 22cf2e7076..047d589689 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Localisation; using osu.Framework.Screens; -using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -20,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; - public override LocalisableString HeaderText => MaintenanceSettingsStrings.StableDirectorySelectHeader; + public override LocalisableString HeaderText => "Please select your osu!stable install location"; public StableDirectorySelectScreen(TaskCompletionSource taskCompletionSource) { From e0f1f1a5e1fe26fa17402a57a8d432b7230e608c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Sep 2022 17:20:47 +0900 Subject: [PATCH 020/295] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 77c29a5d6e..4f69d27df6 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 29e690a024..3c24250300 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 83410b08f6..b57256dec9 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From 90a05f4bed05cb3ed017e9fc3c92d04daddd816b Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 20 Sep 2022 01:40:20 -0700 Subject: [PATCH 021/295] Cap smoke on point count + omit invisible vertices --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 7359f6029e..70913b0851 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Skinning private float totalDistance; private Vector2? lastPosition; - private const double max_duration = 60_000; + private const int max_point_count = 72_000; public override float Height { @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Skinning lastPosition = position; - if (time - SmokeStartTime > max_duration) + if (SmokePoints.Count >= max_point_count) onSmokeEnded(time); } @@ -360,7 +360,7 @@ namespace osu.Game.Rulesets.Osu.Skinning if (Points.Count == 0) return; - QuadBatch ??= renderer.CreateQuadBatch(7200, 10); + QuadBatch ??= renderer.CreateQuadBatch(max_point_count / 10, 10); Texture ??= renderer.WhitePixel; var shader = GetAppropriateShader(renderer); @@ -410,6 +410,9 @@ namespace osu.Game.Rulesets.Osu.Skinning var dir = PointDirection(point); var ortho = dir.PerpendicularLeft; + if (color.A == 0 || scale == 0) + return; + var localTopLeft = point.Position + (Radius * scale * (-ortho - dir)) - PositionOffset; var localTopRight = point.Position + (Radius * scale * (-ortho + dir)) - PositionOffset; var localBotLeft = point.Position + (Radius * scale * (ortho - dir)) - PositionOffset; From 5d73de9021d947346786b356373b6cdd25833556 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 20:20:32 +0900 Subject: [PATCH 022/295] Perform matrix mults on the GPU --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 70913b0851..58ba8ba0b1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -366,14 +366,16 @@ namespace osu.Game.Rulesets.Osu.Skinning var shader = GetAppropriateShader(renderer); renderer.SetBlend(BlendingParameters.Additive); + renderer.PushLocalMatrix(DrawInfo.Matrix); shader.Bind(); Texture.Bind(); UpdateDrawVariables(renderer); - UpdateVertexBuffer(); + UpdateVertexBuffer(renderer); shader.Unbind(); + renderer.PopLocalMatrix(); } protected Color4 ColorAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour @@ -395,7 +397,7 @@ namespace osu.Game.Rulesets.Osu.Skinning TextureRect = Texture.GetTextureRect(); } - protected virtual void UpdateVertexBuffer() + protected virtual void UpdateVertexBuffer(IRenderer renderer) { foreach (var point in Points) drawPointQuad(point); @@ -420,25 +422,25 @@ namespace osu.Game.Rulesets.Osu.Skinning QuadBatch.Add(new TexturedVertex2D { - Position = Vector2Extensions.Transform(localTopLeft, DrawInfo.Matrix), + Position = localTopLeft, TexturePosition = TextureRect.TopLeft, Colour = Color4Extensions.Multiply(ColorAtPosition(localTopLeft), color), }); QuadBatch.Add(new TexturedVertex2D { - Position = Vector2Extensions.Transform(localTopRight, DrawInfo.Matrix), + Position = localTopRight, TexturePosition = TextureRect.TopRight, Colour = Color4Extensions.Multiply(ColorAtPosition(localTopRight), color), }); QuadBatch.Add(new TexturedVertex2D { - Position = Vector2Extensions.Transform(localBotRight, DrawInfo.Matrix), + Position = localBotRight, TexturePosition = TextureRect.BottomRight, Colour = Color4Extensions.Multiply(ColorAtPosition(localBotRight), color), }); QuadBatch.Add(new TexturedVertex2D { - Position = Vector2Extensions.Transform(localBotLeft, DrawInfo.Matrix), + Position = localBotLeft, TexturePosition = TextureRect.BottomLeft, Colour = Color4Extensions.Multiply(ColorAtPosition(localBotLeft), color), }); From f7962c993d88b9072d749cfad8814e43980b8afa Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 20:36:44 +0900 Subject: [PATCH 023/295] Reduce the number of points --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 58ba8ba0b1..80341c5ef1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Skinning private float totalDistance; private Vector2? lastPosition; - private const int max_point_count = 72_000; + private const int max_point_count = 18_000; public override float Height { From 5d3c6efcc52ae54a2755d3bab0d95c948f1e74fa Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 20:38:22 +0900 Subject: [PATCH 024/295] Dispose quad batch --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 80341c5ef1..c07c2f960f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -445,6 +445,12 @@ namespace osu.Game.Rulesets.Osu.Skinning Colour = Color4Extensions.Multiply(ColorAtPosition(localBotLeft), color), }); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + QuadBatch?.Dispose(); + } } } } From 9f23210e7efbea97cda8b56bb73786fb99b8dff3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 20:39:12 +0900 Subject: [PATCH 025/295] Use British-English --- .../Skinning/Default/DefaultSmoke.cs | 2 +- .../Skinning/Legacy/LegacySmoke.cs | 2 +- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 17 ++++++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index 6eebd18305..85d1018b7f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default fadeOutTime = SmokeStartTime + fade_out_speed * (CurrentTime - (SmokeEndTime + fade_out_delay)); } - protected override Color4 PointColor(SmokePoint point) + protected override Color4 PointColour(SmokePoint point) { var color = Color4.White; color.A = alpha; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index d8c3ff3521..d3ce294696 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy finalFadeOutTime = final_fade_out_speed * (CurrentTime - SmokeEndTime) + SmokeEndTime - initialFadeOutDurationTrunc * (1 + 1 / re_fade_in_speed); } - protected override Color4 PointColor(SmokePoint point) + protected override Color4 PointColour(SmokePoint point) { var color = Color4.White; diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index c07c2f960f..63a370981e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -315,7 +315,6 @@ namespace osu.Game.Rulesets.Osu.Skinning protected new Smoke Source => (Smoke)base.Source; protected IVertexBatch? QuadBatch; - protected readonly List Points = new List(); protected float Radius; @@ -378,11 +377,11 @@ namespace osu.Game.Rulesets.Osu.Skinning renderer.PopLocalMatrix(); } - protected Color4 ColorAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour + protected Color4 ColourAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour ? ((SRGBColour)DrawColourInfo.Colour).Linear : DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, DrawSize)).Linear; - protected abstract Color4 PointColor(SmokePoint point); + protected abstract Color4 PointColour(SmokePoint point); protected abstract float PointScale(SmokePoint point); @@ -407,12 +406,12 @@ namespace osu.Game.Rulesets.Osu.Skinning { Debug.Assert(QuadBatch != null); - var color = PointColor(point); + var colour = PointColour(point); float scale = PointScale(point); var dir = PointDirection(point); var ortho = dir.PerpendicularLeft; - if (color.A == 0 || scale == 0) + if (colour.A == 0 || scale == 0) return; var localTopLeft = point.Position + (Radius * scale * (-ortho - dir)) - PositionOffset; @@ -424,25 +423,25 @@ namespace osu.Game.Rulesets.Osu.Skinning { Position = localTopLeft, TexturePosition = TextureRect.TopLeft, - Colour = Color4Extensions.Multiply(ColorAtPosition(localTopLeft), color), + Colour = Color4Extensions.Multiply(ColourAtPosition(localTopLeft), colour), }); QuadBatch.Add(new TexturedVertex2D { Position = localTopRight, TexturePosition = TextureRect.TopRight, - Colour = Color4Extensions.Multiply(ColorAtPosition(localTopRight), color), + Colour = Color4Extensions.Multiply(ColourAtPosition(localTopRight), colour), }); QuadBatch.Add(new TexturedVertex2D { Position = localBotRight, TexturePosition = TextureRect.BottomRight, - Colour = Color4Extensions.Multiply(ColorAtPosition(localBotRight), color), + Colour = Color4Extensions.Multiply(ColourAtPosition(localBotRight), colour), }); QuadBatch.Add(new TexturedVertex2D { Position = localBotLeft, TexturePosition = TextureRect.BottomLeft, - Colour = Color4Extensions.Multiply(ColorAtPosition(localBotLeft), color), + Colour = Color4Extensions.Multiply(ColourAtPosition(localBotLeft), colour), }); } From ff6e4e3a9644c03c211a3221cd263ca4edc97732 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 20:42:12 +0900 Subject: [PATCH 026/295] Privatise setters --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 63a370981e..19891c823d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -314,19 +314,19 @@ namespace osu.Game.Rulesets.Osu.Skinning { protected new Smoke Source => (Smoke)base.Source; - protected IVertexBatch? QuadBatch; + protected IVertexBatch? QuadBatch { get; private set; } protected readonly List Points = new List(); - protected float Radius; - protected Vector2 DrawSize; - protected Vector2 PositionOffset; - protected Texture? Texture; + protected float Radius { get; private set; } + protected Vector2 DrawSize { get; private set; } + protected Vector2 PositionOffset { get; private set; } + protected Texture? Texture { get; private set; } - protected double SmokeStartTime; - protected double SmokeEndTime; - protected double CurrentTime; + protected double SmokeStartTime { get; private set; } + protected double SmokeEndTime { get; private set; } + protected double CurrentTime { get; private set; } - protected RectangleF TextureRect; + protected RectangleF TextureRect { get; private set; } private IFrameBasedClock? clock; From c28ed477e152c4dd1fb082b6aedb16bfffa7b6ec Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 20:54:49 +0900 Subject: [PATCH 027/295] Expose less stuff, clean up implementation --- .../Skinning/Default/DefaultSmoke.cs | 5 +- .../Skinning/Legacy/LegacySmoke.cs | 6 - osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 148 +++++------------- 3 files changed, 44 insertions(+), 115 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index 85d1018b7f..a3c5733eb4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Rendering; using osuTK; using osuTK.Graphics; @@ -34,9 +33,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { } - protected override void UpdateDrawVariables(IRenderer renderer) + public override void ApplyState() { - base.UpdateDrawVariables(renderer); + base.ApplyState(); fadeOutTime = SmokeStartTime + fade_out_speed * (CurrentTime - (SmokeEndTime + fade_out_delay)); } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index d3ce294696..50669cf79a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Rendering; using osu.Framework.Utils; using osu.Game.Skinning; using osuTK; @@ -99,11 +98,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); rotationSeed = Source.RotationSeed; - } - - protected override void UpdateDrawVariables(IRenderer renderer) - { - base.UpdateDrawVariables(renderer); rotationRNG = new Random(rotationSeed); initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime); diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 19891c823d..410ee7b4f0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; -using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Rulesets.Osu.UI; using osuTK; @@ -41,57 +40,11 @@ namespace osu.Game.Rulesets.Osu.Skinning } } - private Texture? texture; + protected Texture? Texture { get; set; } - protected Texture? Texture - { - get => texture; - set - { - texture = value; - Invalidate(Invalidation.DrawNode); - } - } + protected double SmokeStartTime { get; private set; } = double.MinValue; - private double smokeTimeStart = double.MinValue; - - protected double SmokeStartTime - { - get => smokeTimeStart; - private set - { - if (smokeTimeStart == value) - return; - - smokeTimeStart = value; - Invalidate(Invalidation.DrawNode); - } - } - - private double smokeTimeEnd = double.MaxValue; - - protected double SmokeEndTime - { - get => smokeTimeEnd; - private set - { - if (smokeTimeEnd == value) - return; - - smokeTimeEnd = value; - Invalidate(Invalidation.DrawNode); - } - } - - public override IFrameBasedClock Clock - { - get => base.Clock; - set - { - base.Clock = value; - Invalidate(Invalidation.DrawNode); - } - } + protected double SmokeEndTime { get; private set; } = double.MaxValue; private Vector2 topLeft; @@ -104,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Skinning return; topLeft = value; - Invalidate(); + Invalidate(Invalidation.Layout); } } @@ -277,6 +230,8 @@ namespace osu.Game.Rulesets.Osu.Skinning base.Update(); Position = TopLeft; + + Invalidate(Invalidation.DrawNode); } protected override void Dispose(bool isDisposing) @@ -314,21 +269,16 @@ namespace osu.Game.Rulesets.Osu.Skinning { protected new Smoke Source => (Smoke)base.Source; - protected IVertexBatch? QuadBatch { get; private set; } - protected readonly List Points = new List(); - - protected float Radius { get; private set; } - protected Vector2 DrawSize { get; private set; } - protected Vector2 PositionOffset { get; private set; } - protected Texture? Texture { get; private set; } - protected double SmokeStartTime { get; private set; } protected double SmokeEndTime { get; private set; } protected double CurrentTime { get; private set; } - protected RectangleF TextureRect { get; private set; } - - private IFrameBasedClock? clock; + private readonly List points = new List(); + private IVertexBatch? quadBatch; + private float radius; + private Vector2 drawSize; + private Vector2 positionOffset; + private Texture? texture; protected SmokeDrawNode(ITexturedShaderDrawable source) : base(source) @@ -339,28 +289,29 @@ namespace osu.Game.Rulesets.Osu.Skinning { base.ApplyState(); - Points.Clear(); - Points.AddRange(Source.SmokePoints); + points.Clear(); + points.AddRange(Source.SmokePoints); - Radius = Source.Radius; - DrawSize = Source.DrawSize; - PositionOffset = Source.TopLeft; - Texture = Source.Texture; - clock = Source.Clock; + radius = Source.Radius; + drawSize = Source.DrawSize; + positionOffset = Source.TopLeft; + texture = Source.Texture; SmokeStartTime = Source.SmokeStartTime; SmokeEndTime = Source.SmokeEndTime; + CurrentTime = Source.Clock.CurrentTime; } public sealed override void Draw(IRenderer renderer) { base.Draw(renderer); - if (Points.Count == 0) + if (points.Count == 0) return; - QuadBatch ??= renderer.CreateQuadBatch(max_point_count / 10, 10); - Texture ??= renderer.WhitePixel; + quadBatch ??= renderer.CreateQuadBatch(max_point_count / 10, 10); + texture ??= renderer.WhitePixel; + RectangleF textureRect = texture.GetTextureRect(); var shader = GetAppropriateShader(renderer); @@ -368,10 +319,10 @@ namespace osu.Game.Rulesets.Osu.Skinning renderer.PushLocalMatrix(DrawInfo.Matrix); shader.Bind(); - Texture.Bind(); + texture.Bind(); - UpdateDrawVariables(renderer); - UpdateVertexBuffer(renderer); + foreach (var point in points) + drawPointQuad(point, textureRect); shader.Unbind(); renderer.PopLocalMatrix(); @@ -379,7 +330,7 @@ namespace osu.Game.Rulesets.Osu.Skinning protected Color4 ColourAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour ? ((SRGBColour)DrawColourInfo.Colour).Linear - : DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, DrawSize)).Linear; + : DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, drawSize)).Linear; protected abstract Color4 PointColour(SmokePoint point); @@ -387,24 +338,9 @@ namespace osu.Game.Rulesets.Osu.Skinning protected abstract Vector2 PointDirection(SmokePoint point); - protected virtual void UpdateDrawVariables(IRenderer renderer) + private void drawPointQuad(SmokePoint point, RectangleF textureRect) { - Debug.Assert(clock != null); - Debug.Assert(Texture != null); - - CurrentTime = clock.CurrentTime; - TextureRect = Texture.GetTextureRect(); - } - - protected virtual void UpdateVertexBuffer(IRenderer renderer) - { - foreach (var point in Points) - drawPointQuad(point); - } - - private void drawPointQuad(SmokePoint point) - { - Debug.Assert(QuadBatch != null); + Debug.Assert(quadBatch != null); var colour = PointColour(point); float scale = PointScale(point); @@ -414,33 +350,33 @@ namespace osu.Game.Rulesets.Osu.Skinning if (colour.A == 0 || scale == 0) return; - var localTopLeft = point.Position + (Radius * scale * (-ortho - dir)) - PositionOffset; - var localTopRight = point.Position + (Radius * scale * (-ortho + dir)) - PositionOffset; - var localBotLeft = point.Position + (Radius * scale * (ortho - dir)) - PositionOffset; - var localBotRight = point.Position + (Radius * scale * (ortho + dir)) - PositionOffset; + var localTopLeft = point.Position + (radius * scale * (-ortho - dir)) - positionOffset; + var localTopRight = point.Position + (radius * scale * (-ortho + dir)) - positionOffset; + var localBotLeft = point.Position + (radius * scale * (ortho - dir)) - positionOffset; + var localBotRight = point.Position + (radius * scale * (ortho + dir)) - positionOffset; - QuadBatch.Add(new TexturedVertex2D + quadBatch.Add(new TexturedVertex2D { Position = localTopLeft, - TexturePosition = TextureRect.TopLeft, + TexturePosition = textureRect.TopLeft, Colour = Color4Extensions.Multiply(ColourAtPosition(localTopLeft), colour), }); - QuadBatch.Add(new TexturedVertex2D + quadBatch.Add(new TexturedVertex2D { Position = localTopRight, - TexturePosition = TextureRect.TopRight, + TexturePosition = textureRect.TopRight, Colour = Color4Extensions.Multiply(ColourAtPosition(localTopRight), colour), }); - QuadBatch.Add(new TexturedVertex2D + quadBatch.Add(new TexturedVertex2D { Position = localBotRight, - TexturePosition = TextureRect.BottomRight, + TexturePosition = textureRect.BottomRight, Colour = Color4Extensions.Multiply(ColourAtPosition(localBotRight), colour), }); - QuadBatch.Add(new TexturedVertex2D + quadBatch.Add(new TexturedVertex2D { Position = localBotLeft, - TexturePosition = TextureRect.BottomLeft, + TexturePosition = textureRect.BottomLeft, Colour = Color4Extensions.Multiply(ColourAtPosition(localBotLeft), colour), }); } @@ -448,7 +384,7 @@ namespace osu.Game.Rulesets.Osu.Skinning protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - QuadBatch?.Dispose(); + quadBatch?.Dispose(); } } } From 102c1409674733535121b8957d8194c6a2940515 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 20:59:58 +0900 Subject: [PATCH 028/295] Remove another invalidate --- .../Skinning/Legacy/LegacySmoke.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index 50669cf79a..556996895c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -35,20 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private const float max_rotation = 0.25f; - private int rotationSeed = RNG.Next(); - - protected int RotationSeed - { - get => rotationSeed; - set - { - if (rotationSeed == value) - return; - - rotationSeed = value; - Invalidate(Invalidation.DrawNode); - } - } + protected int RotationSeed { get; set; } = RNG.Next(); protected override double LifetimeAfterSmokeEnd { From 3ec16063bdb017b7c07f11ed764023a47c1a4efb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 20 Sep 2022 21:01:18 +0900 Subject: [PATCH 029/295] And another invalidate --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 410ee7b4f0..3afcc031a5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -30,14 +30,7 @@ namespace osu.Game.Rulesets.Osu.Skinning protected float Radius { get => radius ?? Texture?.DisplayWidth * 0.165f ?? 3; - set - { - if (radius == value) - return; - - radius = value; - Invalidate(Invalidation.DrawNode); - } + set => radius = value; } protected Texture? Texture { get; set; } From ddbd69dc678fa10fe237186fc2a6aeaa352a80b7 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 20 Sep 2022 09:44:01 -0700 Subject: [PATCH 030/295] Replace LifetimeAfterSmokeEnd with abstract LifetimeEnd --- osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs | 2 +- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs | 4 ++-- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index a3c5733eb4..65ae490882 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default private const double fade_out_duration = 50; private const float alpha = 0.5f; - protected override double LifetimeAfterSmokeEnd => fade_out_delay + fade_out_duration + (SmokeEndTime - SmokeStartTime) / fade_out_speed; + public override double LifetimeEnd => SmokeEndTime + fade_out_delay + fade_out_duration + (SmokeEndTime - SmokeStartTime) / fade_out_speed; public DefaultSmoke() { diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index 556996895c..f02c20fefb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -37,12 +37,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected int RotationSeed { get; set; } = RNG.Next(); - protected override double LifetimeAfterSmokeEnd + public override double LifetimeEnd { get { double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); - return final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; + return SmokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 3afcc031a5..c381b543b3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -69,7 +69,6 @@ namespace osu.Game.Rulesets.Osu.Skinning } } - protected abstract double LifetimeAfterSmokeEnd { get; } protected virtual float PointInterval => Radius * 7f / 8; protected bool IsActive { get; private set; } @@ -206,6 +205,8 @@ namespace osu.Game.Rulesets.Osu.Skinning BottomRight = new Vector2(BottomRight.X, position.Y); } + public abstract override double LifetimeEnd { get; } + private void onSmokeEnded(double time) { if (!IsActive) @@ -213,7 +214,6 @@ namespace osu.Game.Rulesets.Osu.Skinning IsActive = false; SmokeEndTime = time; - LifetimeEnd = time + LifetimeAfterSmokeEnd + 100; } protected abstract override DrawNode CreateDrawNode(); From 092e6cfa1da050a5d16d0ac86fc95547f14eabbc Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 20 Sep 2022 12:03:07 -0700 Subject: [PATCH 031/295] Lock smoke bounds to playfield --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 86 ++----------------------- 1 file changed, 5 insertions(+), 81 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index c381b543b3..4d2a193a8a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -39,36 +39,6 @@ namespace osu.Game.Rulesets.Osu.Skinning protected double SmokeEndTime { get; private set; } = double.MaxValue; - private Vector2 topLeft; - - protected Vector2 TopLeft - { - get => topLeft; - set - { - if (topLeft == value) - return; - - topLeft = value; - Invalidate(Invalidation.Layout); - } - } - - private Vector2 bottomRight; - - protected Vector2 BottomRight - { - get => bottomRight; - set - { - if (bottomRight == value) - return; - - bottomRight = value; - Invalidate(Invalidation.Layout); - } - } - protected virtual float PointInterval => Radius * 7f / 8; protected bool IsActive { get; private set; } @@ -79,24 +49,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private const int max_point_count = 18_000; - public override float Height - { - get => base.Height = BottomRight.Y - TopLeft.Y; - set => throw new InvalidOperationException($"Cannot manually set {nameof(Height)} of {nameof(Smoke)}."); - } - - public override float Width - { - get => base.Width = BottomRight.X - TopLeft.X; - set => throw new InvalidOperationException($"Cannot manually set {nameof(Width)} of {nameof(Smoke)}."); - } - - public override Vector2 Size - { - get => base.Size = BottomRight - TopLeft; - set => throw new InvalidOperationException($"Cannot manually set {nameof(Size)} of {nameof(Smoke)}."); - } - [Resolved(CanBeNull = true)] private SmokeContainer? smokeContainer { get; set; } @@ -111,8 +63,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { base.LoadComplete(); - Anchor = Anchor.TopLeft; - Origin = Anchor.TopLeft; + RelativeSizeAxes = Axes.Both; SmokeStartTime = Time.Current; @@ -157,7 +108,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { int index = ~SmokePoints.BinarySearch(new SmokePoint { Time = time }, new SmokePoint.UpperBoundComparer()); SmokePoints.RemoveRange(index, SmokePoints.Count - index); - recalculateBounds(); } totalDistance %= PointInterval; @@ -175,7 +125,6 @@ namespace osu.Game.Rulesets.Osu.Skinning } Invalidate(Invalidation.DrawNode); - adaptBounds(position); } lastPosition = position; @@ -184,27 +133,6 @@ namespace osu.Game.Rulesets.Osu.Skinning onSmokeEnded(time); } - private void recalculateBounds() - { - TopLeft = BottomRight = Vector2.Zero; - - foreach (var point in SmokePoints) - adaptBounds(point.Position); - } - - private void adaptBounds(Vector2 position) - { - if (position.X < TopLeft.X) - TopLeft = new Vector2(position.X, TopLeft.Y); - else if (position.X > BottomRight.X) - BottomRight = new Vector2(position.X, BottomRight.Y); - - if (position.Y < TopLeft.Y) - TopLeft = new Vector2(TopLeft.X, position.Y); - else if (position.Y > BottomRight.Y) - BottomRight = new Vector2(BottomRight.X, position.Y); - } - public abstract override double LifetimeEnd { get; } private void onSmokeEnded(double time) @@ -222,8 +150,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { base.Update(); - Position = TopLeft; - Invalidate(Invalidation.DrawNode); } @@ -270,7 +196,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private IVertexBatch? quadBatch; private float radius; private Vector2 drawSize; - private Vector2 positionOffset; private Texture? texture; protected SmokeDrawNode(ITexturedShaderDrawable source) @@ -287,7 +212,6 @@ namespace osu.Game.Rulesets.Osu.Skinning radius = Source.Radius; drawSize = Source.DrawSize; - positionOffset = Source.TopLeft; texture = Source.Texture; SmokeStartTime = Source.SmokeStartTime; @@ -343,10 +267,10 @@ namespace osu.Game.Rulesets.Osu.Skinning if (colour.A == 0 || scale == 0) return; - var localTopLeft = point.Position + (radius * scale * (-ortho - dir)) - positionOffset; - var localTopRight = point.Position + (radius * scale * (-ortho + dir)) - positionOffset; - var localBotLeft = point.Position + (radius * scale * (ortho - dir)) - positionOffset; - var localBotRight = point.Position + (radius * scale * (ortho + dir)) - positionOffset; + var localTopLeft = point.Position + (radius * scale * (-ortho - dir)); + var localTopRight = point.Position + (radius * scale * (-ortho + dir)); + var localBotLeft = point.Position + (radius * scale * (ortho - dir)); + var localBotRight = point.Position + (radius * scale * (ortho + dir)); quadBatch.Add(new TexturedVertex2D { From 6a0047b7a224dee2b012ffc922c8d73a2c2a55f6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 22 Sep 2022 21:01:23 +0900 Subject: [PATCH 032/295] Update location of FullscreenCapability bindable --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 4 ++-- osu.Game/Screens/Utility/LatencyCertifierScreen.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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/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(); From 0fa5e1b28e0e8d9b00ebe6d85fd6517b958a5656 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Sep 2022 20:18:44 +0900 Subject: [PATCH 033/295] Fix intermittent leaderboard crash due to request finishing after drawable is disposed --- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 0f6ac2b455..ffb13dc458 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -153,10 +153,11 @@ namespace osu.Game.Screens.Select.Leaderboards var req = new GetScoresRequest(fetchBeatmapInfo, fetchRuleset, Scope, requestMods); - req.Success += r => SetScores( + // Schedule is required to avoid potential object disposed exception when LoadComponentAsync is eventually called. + req.Success += r => Schedule(() => SetScores( scoreManager.OrderByTotalScore(r.Scores.Select(s => s.ToScoreInfo(rulesets, fetchBeatmapInfo))), r.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo) - ); + )); return req; } From 0e38ff07c74de55cfc640361f51b8ea5658171d9 Mon Sep 17 00:00:00 2001 From: NullifiedJosh <86538544+NullifiedJosh@users.noreply.github.com> Date: Fri, 23 Sep 2022 20:19:56 +0800 Subject: [PATCH 034/295] Check if relax is one of the mods, if so hide. --- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index ef2936ac94..07968e4bee 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Input; using osu.Game.Beatmaps; @@ -26,9 +27,15 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; + private bool showMobileMapper = true; + public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { + // Check if mods have RelaxMod instance + if (mods.OfType().Any()) + showMobileMapper = false; + Direction.Value = ScrollingDirection.Down; TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450); } @@ -36,7 +43,8 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - KeyBindingInputManager.Add(new CatchTouchInputMapper()); + if (showMobileMapper) + KeyBindingInputManager.Add(new CatchTouchInputMapper()); } protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); From 4bd92b89bcee77c01a7050648020518018e3f8e8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Sep 2022 16:29:19 +0300 Subject: [PATCH 035/295] Change test scene to ensure progress notification is closed on completion --- .../Visual/UserInterface/TestSceneNotificationOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index b314d95597..47958f4a41 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -465,7 +465,7 @@ namespace osu.Game.Tests.Visual.UserInterface { base.Update(); - progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed); + progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed && n.WasClosed); if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) { From cdc3afac88b3ac203ab6999ce2c8dbdc37906909 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Sep 2022 16:30:07 +0300 Subject: [PATCH 036/295] Fix update progress notification not closing on completion --- osu.Game/Updater/UpdateManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 100464029b..7b540cb564 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -156,6 +156,7 @@ namespace osu.Game.Updater switch (State) { case ProgressNotificationState.Cancelled: + case ProgressNotificationState.Completed: base.Close(runFlingAnimation); break; } From 6c8e587344581907013d223200c9b4dd3b10e96e Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 24 Sep 2022 21:52:16 +0300 Subject: [PATCH 037/295] Move margin value to constant --- osu.Game/Overlays/NowPlayingOverlay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 6eddc7da83..687731d068 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -38,6 +38,7 @@ namespace osu.Game.Overlays private const float transition_length = 800; private const float progress_height = 10; private const float bottom_black_area_height = 55; + private const float margin = 10; private Drawable background; private ProgressBar progressBar; @@ -69,7 +70,7 @@ namespace osu.Game.Overlays public NowPlayingOverlay() { Width = 400; - Margin = new MarginPadding(10); + Margin = new MarginPadding(margin); } [BackgroundDependencyLoader] @@ -194,7 +195,7 @@ namespace osu.Game.Overlays LoadComponentAsync(playlist = new PlaylistOverlay { RelativeSizeAxes = Axes.X, - Y = player_height + 10, + Y = player_height + margin, }, _ => { dragContainer.Add(playlist); From 5d0b2d34c9289eb79a26aa58e238ee24c56e14f0 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 24 Sep 2022 23:35:06 +0300 Subject: [PATCH 038/295] Make playlist to always be in screen bounds --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 5 ++-- osu.Game/Overlays/NowPlayingOverlay.cs | 27 +++++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 9fe2fd5279..63f1aa248c 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -8,6 +8,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; @@ -24,7 +25,7 @@ namespace osu.Game.Overlays.Music public class PlaylistOverlay : VisibilityContainer { private const float transition_duration = 600; - private const float playlist_height = 510; + public const float PLAYLIST_HEIGHT = 510; private readonly BindableList> beatmapSets = new BindableList>(); @@ -130,7 +131,7 @@ namespace osu.Game.Overlays.Music filter.Search.HoldFocus = true; Schedule(() => filter.Search.TakeFocus()); - this.ResizeTo(new Vector2(1, playlist_height), transition_duration, Easing.OutQuint); + this.ResizeTo(new Vector2(1, RelativeSizeAxes.HasFlagFast(Axes.Y) ? 1f : PLAYLIST_HEIGHT), transition_duration, Easing.OutQuint); this.FadeIn(transition_duration, Easing.OutQuint); } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 687731d068..900b4bebf0 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -54,6 +54,7 @@ namespace osu.Game.Overlays private Container dragContainer; private Container playerContainer; + private Container playlistContainer; protected override string PopInSampleName => "UI/now-playing-pop-in"; protected override string PopOutSampleName => "UI/now-playing-pop-out"; @@ -83,7 +84,6 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Children = new Drawable[] { playerContainer = new Container @@ -183,8 +183,13 @@ namespace osu.Game.Overlays } }, }, + playlistContainer = new Container + { + RelativeSizeAxes = Axes.X, + Y = player_height + margin, + } } - } + }, }; } @@ -194,11 +199,10 @@ namespace osu.Game.Overlays { LoadComponentAsync(playlist = new PlaylistOverlay { - RelativeSizeAxes = Axes.X, - Y = player_height + margin, + RelativeSizeAxes = Axes.Both, }, _ => { - dragContainer.Add(playlist); + playlistContainer.Add(playlist); playlist.State.BindValueChanged(s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint), true); @@ -243,7 +247,18 @@ namespace osu.Game.Overlays { base.UpdateAfterChildren(); - Height = dragContainer.Height; + playlistContainer.Height = MathF.Min(Parent.DrawHeight - margin * 3 - player_height, PlaylistOverlay.PLAYLIST_HEIGHT); + + float height = player_height; + + if (playlist != null) + { + height += playlist.DrawHeight; + if (playlist.State.Value == Visibility.Visible) + height += margin; + } + + Height = dragContainer.Height = height; } protected override void Update() From 38f0524e88168b5ef0d175207fd938eb2251ea4b Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 25 Sep 2022 18:00:28 +0200 Subject: [PATCH 039/295] build: harden ci.yml permissions Signed-off-by: Alex --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 082e0d247c..33ec3d6602 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read # to fetch code (actions/checkout) + jobs: inspect-code: name: Code Quality From 90dd3c3020c5df90fb48980a4fb9de03dd3b9d52 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 25 Sep 2022 18:03:13 +0200 Subject: [PATCH 040/295] build: harden report-nunit.yml permissions Signed-off-by: Alex --- .github/workflows/report-nunit.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/report-nunit.yml b/.github/workflows/report-nunit.yml index 358cbda17a..bfc9620174 100644 --- a/.github/workflows/report-nunit.yml +++ b/.github/workflows/report-nunit.yml @@ -8,8 +8,12 @@ on: workflows: ["Continuous Integration"] types: - completed +permissions: {} jobs: annotate: + permissions: + checks: write # to create checks (dorny/test-reporter) + name: Annotate CI run with test results runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion != 'cancelled' }} From 68f62ca9cd653f0e9175be716ebd2ac492c3b6f7 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 25 Sep 2022 18:04:27 +0200 Subject: [PATCH 041/295] build: harden sentry-release.yml permissions Signed-off-by: Alex --- .github/workflows/sentry-release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/sentry-release.yml b/.github/workflows/sentry-release.yml index 442b97c473..cce3f23e5f 100644 --- a/.github/workflows/sentry-release.yml +++ b/.github/workflows/sentry-release.yml @@ -5,6 +5,9 @@ on: tags: - '*' +permissions: + contents: read # to fetch code (actions/checkout) + jobs: sentry_release: runs-on: ubuntu-latest From 537796c3a17fb049a4b417c1f6835e4ffd35d208 Mon Sep 17 00:00:00 2001 From: o-dasher <88356162+o-dasher@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:49:22 -0400 Subject: [PATCH 042/295] Cleanup mod bindings --- .../Mods/CatchModFlashlight.cs | 8 +------ .../Mods/CatchModNoScope.cs | 4 +--- .../Mods/ManiaModFlashlight.cs | 10 ++------- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 4 +--- .../Mods/OsuModFlashlight.cs | 10 ++------- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 4 +--- osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs | 4 +--- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 4 +--- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 6 +---- .../Mods/TaikoModFlashlight.cs | 10 ++------- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 14 +++--------- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 4 +--- osu.Game/Rulesets/Mods/ModHalfTime.cs | 4 +--- osu.Game/Rulesets/Mods/ModMuted.cs | 22 ++++--------------- osu.Game/Rulesets/Mods/ModRandom.cs | 6 +---- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 4 +--- osu.Game/Rulesets/Mods/ModWindDown.cs | 14 +++--------- osu.Game/Rulesets/Mods/ModWindUp.cs | 14 +++--------- 18 files changed, 30 insertions(+), 116 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index abe391ba4e..e8b126cfed 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -21,17 +21,11 @@ namespace osu.Game.Rulesets.Catch.Mods { MinValue = 0.5f, MaxValue = 1.5f, - Default = 1f, - Value = 1f, Precision = 0.1f }; [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] - public override BindableBool ComboBasedSize { get; } = new BindableBool - { - Default = true, - Value = true - }; + public override BindableBool ComboBasedSize { get; } = new BindableBool(true); public override float DefaultFlashlightSize => 350; diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs b/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs index 9038153e20..1b781b032c 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs @@ -22,10 +22,8 @@ namespace osu.Game.Rulesets.Catch.Mods "The combo count at which the catcher becomes completely hidden", SettingControlType = typeof(SettingsSlider) )] - public override BindableInt HiddenComboCount { get; } = new BindableInt + public override BindableInt HiddenComboCount { get; } = new BindableInt(10) { - Default = 10, - Value = 10, MinValue = 0, MaxValue = 50, }; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs index 8ef5bfd94c..72ea1a2431 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs @@ -18,21 +18,15 @@ namespace osu.Game.Rulesets.Mania.Mods public override Type[] IncompatibleMods => new[] { typeof(ModHidden) }; [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] - public override BindableFloat SizeMultiplier { get; } = new BindableFloat + public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, MaxValue = 3f, - Default = 1f, - Value = 1f, Precision = 0.1f }; [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] - public override BindableBool ComboBasedSize { get; } = new BindableBool - { - Default = false, - Value = false - }; + public override BindableBool ComboBasedSize { get; } = new BindableBool(); public override float DefaultFlashlightSize => 50; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index e624660410..b2980f5311 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -19,12 +19,10 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Hit them at the right size!"; [SettingSource("Starting Size", "The initial size multiplier applied to all objects.")] - public override BindableNumber StartScale { get; } = new BindableFloat + public override BindableNumber StartScale { get; } = new BindableFloat(2) { MinValue = 1f, MaxValue = 25f, - Default = 2f, - Value = 2f, Precision = 0.1f, }; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index e5a458488e..7b34fe05a9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -33,21 +33,15 @@ namespace osu.Game.Rulesets.Osu.Mods }; [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] - public override BindableFloat SizeMultiplier { get; } = new BindableFloat + public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, MaxValue = 2f, - Default = 1f, - Value = 1f, Precision = 0.1f }; [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] - public override BindableBool ComboBasedSize { get; } = new BindableBool - { - Default = true, - Value = true - }; + public override BindableBool ComboBasedSize { get; } = new BindableBool(true); public override float DefaultFlashlightSize => 180; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index b77c887cd3..b49257e289 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -19,12 +19,10 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Hit them at the right size!"; [SettingSource("Starting Size", "The initial size multiplier applied to all objects.")] - public override BindableNumber StartScale { get; } = new BindableFloat + public override BindableNumber StartScale { get; } = new BindableFloat(0.5f) { MinValue = 0f, MaxValue = 0.99f, - Default = 0.5f, - Value = 0.5f, Precision = 0.01f, }; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index 817f7b599c..84538cec30 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -27,10 +27,8 @@ namespace osu.Game.Rulesets.Osu.Mods "The combo count at which the cursor becomes completely hidden", SettingControlType = typeof(SettingsSlider) )] - public override BindableInt HiddenComboCount { get; } = new BindableInt + public override BindableInt HiddenComboCount { get; } = new BindableInt(10) { - Default = 10, - Value = 10, MinValue = 0, MaxValue = 50, }; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 78c7aa53f4..618fcfe05d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -29,10 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray(); [SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(SettingsSlider))] - public BindableFloat AngleSharpness { get; } = new BindableFloat + public BindableFloat AngleSharpness { get; } = new BindableFloat(7) { - Default = 7, - Value = 7, MinValue = 1, MaxValue = 10, Precision = 0.1f diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 861ad80b7f..406968ba08 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -53,11 +53,7 @@ namespace osu.Game.Rulesets.Osu.Mods }).ToArray(); [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] - public Bindable Seed { get; } = new Bindable - { - Default = null, - Value = null - }; + public Bindable Seed { get; } = new Bindable(); [SettingSource("Metronome ticks", "Whether a metronome beat should play in the background")] public Bindable Metronome { get; } = new BindableBool(true); diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 66616486df..05baf88a33 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -18,21 +18,15 @@ namespace osu.Game.Rulesets.Taiko.Mods public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] - public override BindableFloat SizeMultiplier { get; } = new BindableFloat + public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, MaxValue = 1.5f, - Default = 1f, - Value = 1f, Precision = 0.1f }; [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] - public override BindableBool ComboBasedSize { get; } = new BindableBool - { - Default = true, - Value = true - }; + public override BindableBool ComboBasedSize { get; } = new BindableBool(true); public override float DefaultFlashlightSize => 250; diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 697b303689..e7127abcf0 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -36,32 +36,24 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp), typeof(ModAutoplay) }; [SettingSource("Initial rate", "The starting speed of the track")] - public BindableNumber InitialRate { get; } = new BindableDouble + public BindableNumber InitialRate { get; } = new BindableDouble(1) { MinValue = 0.5, MaxValue = 2, - Default = 1, - Value = 1, Precision = 0.01 }; [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] - public BindableBool AdjustPitch { get; } = new BindableBool - { - Default = true, - Value = true - }; + public BindableBool AdjustPitch { get; } = new BindableBool(true); /// /// The instantaneous rate of the track. /// Every frame this mod will attempt to smoothly adjust this to meet . /// - public BindableNumber SpeedChange { get; } = new BindableDouble + public BindableNumber SpeedChange { get; } = new BindableDouble(1) { MinValue = min_allowable_rate, MaxValue = max_allowable_rate, - Default = 1, - Value = 1 }; // The two constants below denote the maximum allowable range of rates that `SpeedChange` can take. diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index d8a41ae658..9e4469bf25 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -18,12 +18,10 @@ namespace osu.Game.Rulesets.Mods public override LocalisableString Description => "Zoooooooooom..."; [SettingSource("Speed increase", "The actual increase to apply")] - public override BindableNumber SpeedChange { get; } = new BindableDouble + public override BindableNumber SpeedChange { get; } = new BindableDouble(1.5) { MinValue = 1.01, MaxValue = 2, - Default = 1.5, - Value = 1.5, Precision = 0.01, }; } diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 8d8b97e79e..7d858dca6f 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -18,12 +18,10 @@ namespace osu.Game.Rulesets.Mods public override LocalisableString Description => "Less zoom..."; [SettingSource("Speed decrease", "The actual decrease to apply")] - public override BindableNumber SpeedChange { get; } = new BindableDouble + public override BindableNumber SpeedChange { get; } = new BindableDouble(0.75) { MinValue = 0.5, MaxValue = 0.99, - Default = 0.75, - Value = 0.75, Precision = 0.01, }; } diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 9735d6b536..05ecd37000 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -36,34 +36,20 @@ namespace osu.Game.Rulesets.Mods private readonly BindableNumber currentCombo = new BindableInt(); [SettingSource("Enable metronome", "Add a metronome beat to help you keep track of the rhythm.")] - public BindableBool EnableMetronome { get; } = new BindableBool - { - Default = true, - Value = true - }; + public BindableBool EnableMetronome { get; } = new BindableBool(true); [SettingSource("Final volume at combo", "The combo count at which point the track reaches its final volume.", SettingControlType = typeof(SettingsSlider))] - public BindableInt MuteComboCount { get; } = new BindableInt + public BindableInt MuteComboCount { get; } = new BindableInt(100) { - Default = 100, - Value = 100, MinValue = 0, MaxValue = 500, }; [SettingSource("Start muted", "Increase volume as combo builds.")] - public BindableBool InverseMuting { get; } = new BindableBool - { - Default = false, - Value = false - }; + public BindableBool InverseMuting { get; } = new BindableBool(); [SettingSource("Mute hit sounds", "Hit sounds are also muted alongside the track.")] - public BindableBool AffectsHitSounds { get; } = new BindableBool - { - Default = true, - Value = true - }; + public BindableBool AffectsHitSounds { get; } = new BindableBool(true); protected ModMuted() { diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index 1f7742b075..178b9fb619 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -18,10 +18,6 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] - public Bindable Seed { get; } = new Bindable - { - Default = null, - Value = null - }; + public Bindable Seed { get; } = new Bindable(); } } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 72a7f4b9a3..c4cb41fb6a 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -39,10 +39,8 @@ namespace osu.Game.Rulesets.Mods private double finalRateTime; private double beginRampTime; - public BindableNumber SpeedChange { get; } = new BindableDouble + public BindableNumber SpeedChange { get; } = new BindableDouble(1) { - Default = 1, - Value = 1, Precision = 0.01, }; diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index 22ed7c2efd..15ee37d481 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -18,31 +18,23 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown; [SettingSource("Initial rate", "The starting speed of the track")] - public override BindableNumber InitialRate { get; } = new BindableDouble + public override BindableNumber InitialRate { get; } = new BindableDouble(1) { MinValue = 0.51, MaxValue = 2, - Default = 1, - Value = 1, Precision = 0.01, }; [SettingSource("Final rate", "The speed increase to ramp towards")] - public override BindableNumber FinalRate { get; } = new BindableDouble + public override BindableNumber FinalRate { get; } = new BindableDouble(0.75) { MinValue = 0.5, MaxValue = 1.99, - Default = 0.75, - Value = 0.75, Precision = 0.01, }; [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] - public override BindableBool AdjustPitch { get; } = new BindableBool - { - Default = true, - Value = true - }; + public override BindableBool AdjustPitch { get; } = new BindableBool(true); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray(); diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index 13ece6d9a3..6d8838c5a5 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -18,31 +18,23 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp; [SettingSource("Initial rate", "The starting speed of the track")] - public override BindableNumber InitialRate { get; } = new BindableDouble + public override BindableNumber InitialRate { get; } = new BindableDouble(1) { MinValue = 0.5, MaxValue = 1.99, - Default = 1, - Value = 1, Precision = 0.01, }; [SettingSource("Final rate", "The speed increase to ramp towards")] - public override BindableNumber FinalRate { get; } = new BindableDouble + public override BindableNumber FinalRate { get; } = new BindableDouble(1.5) { MinValue = 0.51, MaxValue = 2, - Default = 1.5, - Value = 1.5, Precision = 0.01, }; [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] - public override BindableBool AdjustPitch { get; } = new BindableBool - { - Default = true, - Value = true - }; + public override BindableBool AdjustPitch { get; } = new BindableBool(true); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray(); From feadac1f790fec56a1954cdab86f4deccfb9d68d Mon Sep 17 00:00:00 2001 From: o-dasher <88356162+o-dasher@users.noreply.github.com> Date: Sun, 25 Sep 2022 16:02:40 -0400 Subject: [PATCH 043/295] Cleanup all other leftover binding instantiation --- .../Online/TestAPIModJsonSerialization.cs | 14 +++----------- .../Online/TestAPIModMessagePackSerialization.cs | 14 +++----------- .../Visual/Settings/TestSceneSettingsItem.cs | 12 ++---------- .../Visual/Settings/TestSceneSettingsSource.cs | 6 +----- .../UserInterface/TestSceneExpandingContainer.cs | 8 ++------ .../ControlPoints/DifficultyControlPoint.cs | 1 - .../Beatmaps/ControlPoints/EffectControlPoint.cs | 1 - .../Beatmaps/ControlPoints/SampleControlPoint.cs | 1 - .../Beatmaps/ControlPoints/TimingControlPoint.cs | 1 - .../UI/Scrolling/DrawableScrollingRuleset.cs | 1 - .../Screens/Play/MasterGameplayClockContainer.cs | 1 - .../Play/PlayerSettings/BeatmapOffsetControl.cs | 2 -- .../Play/PlayerSettings/PlaybackSettings.cs | 1 - 13 files changed, 11 insertions(+), 52 deletions(-) diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 17709fb10f..da250c1e05 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -204,31 +204,23 @@ namespace osu.Game.Tests.Online public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] - public override BindableNumber InitialRate { get; } = new BindableDouble + public override BindableNumber InitialRate { get; } = new BindableDouble(1.5) { MinValue = 1, MaxValue = 2, - Default = 1.5, - Value = 1.5, Precision = 0.01, }; [SettingSource("Final rate", "The speed increase to ramp towards")] - public override BindableNumber FinalRate { get; } = new BindableDouble + public override BindableNumber FinalRate { get; } = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1, - Default = 0.5, - Value = 0.5, Precision = 0.01, }; [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] - public override BindableBool AdjustPitch { get; } = new BindableBool - { - Default = true, - Value = true - }; + public override BindableBool AdjustPitch { get; } = new BindableBool(true); } private class TestModDifficultyAdjust : ModDifficultyAdjust diff --git a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs index b17414e026..1d8cbffcdb 100644 --- a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs @@ -124,31 +124,23 @@ namespace osu.Game.Tests.Online public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] - public override BindableNumber InitialRate { get; } = new BindableDouble + public override BindableNumber InitialRate { get; } = new BindableDouble(1.5) { MinValue = 1, MaxValue = 2, - Default = 1.5, - Value = 1.5, Precision = 0.01, }; [SettingSource("Final rate", "The speed increase to ramp towards")] - public override BindableNumber FinalRate { get; } = new BindableDouble + public override BindableNumber FinalRate { get; } = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1, - Default = 0.5, - Value = 0.5, Precision = 0.01, }; [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] - public override BindableBool AdjustPitch { get; } = new BindableBool - { - Default = true, - Value = true - }; + public override BindableBool AdjustPitch { get; } = new BindableBool(true); } private class TestModEnum : Mod diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs index b3d1966511..6ff53663ba 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs @@ -29,11 +29,7 @@ namespace osu.Game.Tests.Visual.Settings { Child = textBox = new SettingsTextBox { - Current = new Bindable - { - Default = "test", - Value = "test" - } + Current = new Bindable("test") }; }); AddUntilStep("wait for loaded", () => textBox.IsLoaded); @@ -59,11 +55,7 @@ namespace osu.Game.Tests.Visual.Settings { Child = textBox = new SettingsTextBox { - Current = new Bindable - { - Default = "test", - Value = "test" - } + Current = new Bindable("test") }; }); AddUntilStep("wait for loaded", () => textBox.IsLoaded); diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs index dc2a687bd5..3cf6f7febf 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs @@ -67,11 +67,7 @@ namespace osu.Game.Tests.Visual.Settings }; [SettingSource("Sample number textbox", "Textbox number entry", SettingControlType = typeof(SettingsNumberBox))] - public Bindable IntTextBoxBindable { get; } = new Bindable - { - Default = null, - Value = null - }; + public Bindable IntTextBoxBindable { get; } = new Bindable(); } private enum TestEnum diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs index 704185b117..20f173ab04 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs @@ -38,20 +38,16 @@ namespace osu.Game.Tests.Visual.UserInterface { slider1 = new ExpandableSlider> { - Current = new BindableFloat + Current = new BindableFloat(1.0f) { - Default = 1.0f, - MinValue = 1.0f, MaxValue = 10.0f, Precision = 0.01f, }, }, slider2 = new ExpandableSlider { - Current = new BindableDouble + Current = new BindableDouble(1.0) { - Default = 1.0, - MinValue = 1.0, MaxValue = 10.0, Precision = 0.01, }, diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index c199d1da59..e6f1609d7f 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -24,7 +24,6 @@ namespace osu.Game.Beatmaps.ControlPoints public readonly BindableDouble SliderVelocityBindable = new BindableDouble(1) { Precision = 0.01, - Default = 1, MinValue = 0.1, MaxValue = 10 }; diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index ead07b4eaa..7c4313a015 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -28,7 +28,6 @@ namespace osu.Game.Beatmaps.ControlPoints public readonly BindableDouble ScrollSpeedBindable = new BindableDouble(1) { Precision = 0.01, - Default = 1, MinValue = 0.01, MaxValue = 10 }; diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 78dec67937..c454439c5c 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -45,7 +45,6 @@ namespace osu.Game.Beatmaps.ControlPoints { MinValue = 0, MaxValue = 100, - Default = 100 }; /// diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 23d4d10fd8..61cc060594 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -49,7 +49,6 @@ namespace osu.Game.Beatmaps.ControlPoints /// public readonly BindableDouble BeatLengthBindable = new BindableDouble(DEFAULT_BEAT_LENGTH) { - Default = DEFAULT_BEAT_LENGTH, MinValue = 6, MaxValue = 60000 }; diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index d8a8a6ccd8..825aba5bc2 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -60,7 +60,6 @@ namespace osu.Game.Rulesets.UI.Scrolling /// protected readonly BindableDouble TimeRange = new BindableDouble(time_span_default) { - Default = time_span_default, MinValue = time_span_min, MaxValue = time_span_max }; diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 047f25a111..c3c351ac36 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -34,7 +34,6 @@ namespace osu.Game.Screens.Play public readonly BindableNumber UserPlaybackRate = new BindableDouble(1) { - Default = 1, MinValue = 0.5, MaxValue = 2, Precision = 0.1, diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 75da8e7b9d..537f4d811a 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -31,8 +31,6 @@ namespace osu.Game.Screens.Play.PlayerSettings public BindableDouble Current { get; } = new BindableDouble { - Default = 0, - Value = 0, MinValue = -50, MaxValue = 50, Precision = 0.1, diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 12646d656a..14e3123028 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -17,7 +17,6 @@ namespace osu.Game.Screens.Play.PlayerSettings public readonly Bindable UserPlaybackRate = new BindableDouble(1) { - Default = 1, MinValue = 0.5, MaxValue = 2, Precision = 0.1, From 578f1e1e6c16fbf1001e4b5a8155ff35250fa695 Mon Sep 17 00:00:00 2001 From: o-dasher <88356162+o-dasher@users.noreply.github.com> Date: Sun, 25 Sep 2022 16:28:03 -0400 Subject: [PATCH 044/295] Fixes SizeMultiplier binding --- osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index e8b126cfed..41d4590aa8 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Mods public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] - public override BindableFloat SizeMultiplier { get; } = new BindableFloat + public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, MaxValue = 1.5f, From a9318e5c0418eba7867b43732005160e3ab58162 Mon Sep 17 00:00:00 2001 From: o-dasher <88356162+o-dasher@users.noreply.github.com> Date: Sun, 25 Sep 2022 16:31:04 -0400 Subject: [PATCH 045/295] Fixes small oversight was applying a value when it wasn't supposed to be applied. --- .../Visual/UserInterface/TestSceneExpandingContainer.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs index 20f173ab04..04ca9accac 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs @@ -38,16 +38,20 @@ namespace osu.Game.Tests.Visual.UserInterface { slider1 = new ExpandableSlider> { - Current = new BindableFloat(1.0f) + Current = new BindableFloat { + Default = 1.0f, + MinValue = 1.0f, MaxValue = 10.0f, Precision = 0.01f, }, }, slider2 = new ExpandableSlider { - Current = new BindableDouble(1.0) + Current = new BindableDouble { + Default = 1.0, + MinValue = 1.0 MaxValue = 10.0, Precision = 0.01, }, From ced72126601f19287be2bbc8bfc52f59be8bd4ea Mon Sep 17 00:00:00 2001 From: o-dasher <88356162+o-dasher@users.noreply.github.com> Date: Sun, 25 Sep 2022 16:33:39 -0400 Subject: [PATCH 046/295] Fixes syntax error --- .../Visual/UserInterface/TestSceneExpandingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs index 04ca9accac..704185b117 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface Current = new BindableDouble { Default = 1.0, - MinValue = 1.0 + MinValue = 1.0, MaxValue = 10.0, Precision = 0.01, }, From a7449380cda7920f23cbb6d2f034ec634dcdba16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 14:11:59 +0900 Subject: [PATCH 047/295] Fix osu! slider ticks appearing too late --- osu.Game.Rulesets.Osu/Objects/SliderTick.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs index 569e9b7c1c..676ff62455 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects // This is so on repeats ticks don't appear too late to be visually processed by the player. offset = 200; else - offset = TimeFadeIn * 0.66f; + offset = TimePreempt * 0.66f; TimePreempt = (StartTime - SpanStartTime) / 2 + offset; } From 82d4689716e81d09c3a58596de54c288fbb45e02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 14:30:31 +0900 Subject: [PATCH 048/295] Add failing test showing progress notifications can be flung and cancelled --- .../TestSceneNotificationOverlay.cs | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index b314d95597..895e62d41c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -107,9 +107,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("start drag", () => { - InputManager.MoveMouseTo(notification.ChildrenOfType().Single()); + InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType().Single()); InputManager.PressButton(MouseButton.Left); - InputManager.MoveMouseTo(notification.ChildrenOfType().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0)); + InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0)); }); AddStep("fling away", () => @@ -123,6 +123,45 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("unread count zero", () => notificationOverlay.UnreadCount.Value == 0); } + [Test] + public void TestProgressNotificationCantBeFlung() + { + bool activated = false; + ProgressNotification notification = null!; + + AddStep("post", () => + { + activated = false; + notificationOverlay.Post(notification = new ProgressNotification + { + Text = @"Uploading to BSS...", + CompletionText = "Uploaded to BSS!", + Activated = () => activated = true, + }); + + progressingNotifications.Add(notification); + }); + + AddStep("start drag", () => + { + InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType().Single()); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0)); + }); + + AddStep("attempt fling", () => + { + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddUntilStep("was not closed", () => !notification.WasClosed); + AddUntilStep("was not cancelled", () => notification.State == ProgressNotificationState.Active); + AddAssert("was not activated", () => !activated); + AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + + AddUntilStep("was completed", () => notification.State == ProgressNotificationState.Completed); + } + [Test] public void TestDismissWithoutActivationCloseButton() { From b6dd8168d29313dafdfaeccc90bb4420896df7a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 14:30:40 +0900 Subject: [PATCH 049/295] Fix progress notifications being able to be flung --- osu.Game/Overlays/Notifications/Notification.cs | 4 +++- osu.Game/Overlays/Notifications/ProgressNotification.cs | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index ea654e1272..8be9d2072b 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -68,6 +68,8 @@ namespace osu.Game.Overlays.Notifications public virtual bool Read { get; set; } + protected virtual bool AllowFlingDismiss => true; + public new bool IsDragged => dragContainer.IsDragged; protected virtual IconUsage CloseButtonIcon => FontAwesome.Solid.Check; @@ -315,7 +317,7 @@ namespace osu.Game.Overlays.Notifications protected override void OnDragEnd(DragEndEvent e) { - if (Rotation < -10 || velocity.X < -0.3f) + if (notification.AllowFlingDismiss && (Rotation < -10 || velocity.X < -0.3f)) notification.Close(true); else if (X > 30 || velocity.X > 0.3f) notification.ForwardToOverlay?.Invoke(); diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 61bb22041e..4cf47013bd 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -25,6 +25,8 @@ namespace osu.Game.Overlays.Notifications public Func? CancelRequested { get; set; } + protected override bool AllowFlingDismiss => false; + /// /// The function to post completion notifications back to. /// From 4bd96108c01dc2453f7570475f2a04ac6ac463fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 14:45:17 +0900 Subject: [PATCH 050/295] Fix crash on game exit due to incorrect scheduling of realm change handler --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 82f49e0aef..4963de7251 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -123,7 +123,7 @@ namespace osu.Game.Overlays.FirstRunSetup beatmapSubscription?.Dispose(); } - private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception error) + private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception error) => Schedule(() => { currentlyLoadedBeatmaps.Text = FirstRunSetupBeatmapScreenStrings.CurrentlyLoadedBeatmaps(sender.Count); @@ -139,7 +139,7 @@ namespace osu.Game.Overlays.FirstRunSetup currentlyLoadedBeatmaps.ScaleTo(1.1f) .ScaleTo(1, 1500, Easing.OutQuint); } - } + }); private void downloadTutorial() { From c4887269f7b9096e038bd30bd50e2dd6b489e1d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 14:55:23 +0900 Subject: [PATCH 051/295] Avoid performing a realm write if there are no changes pending in `RulesetConfigManager` --- osu.Game/Rulesets/Configuration/RulesetConfigManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 5a03d66b84..4ff4f66665 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -58,6 +58,9 @@ namespace osu.Game.Rulesets.Configuration pendingWrites.Clear(); } + if (!changed.Any()) + return true; + realm?.Write(r => { foreach (var c in changed) From 43c16cb613cafcb35c1d862214322aff3e17b875 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 15:10:02 +0900 Subject: [PATCH 052/295] Fix potential crash from incorrect drawable mutation in tablet settings display --- .../Settings/Sections/Input/TabletSettings.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 271438ed14..544259ddf1 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -215,21 +215,21 @@ namespace osu.Game.Overlays.Settings.Sections.Input rotation.BindTo(tabletHandler.Rotation); areaOffset.BindTo(tabletHandler.AreaOffset); - areaOffset.BindValueChanged(val => + areaOffset.BindValueChanged(val => Schedule(() => { offsetX.Value = val.NewValue.X; offsetY.Value = val.NewValue.Y; - }, true); + }), true); offsetX.BindValueChanged(val => areaOffset.Value = new Vector2(val.NewValue, areaOffset.Value.Y)); offsetY.BindValueChanged(val => areaOffset.Value = new Vector2(areaOffset.Value.X, val.NewValue)); areaSize.BindTo(tabletHandler.AreaSize); - areaSize.BindValueChanged(val => + areaSize.BindValueChanged(val => Schedule(() => { sizeX.Value = val.NewValue.X; sizeY.Value = val.NewValue.Y; - }, true); + }), true); sizeX.BindValueChanged(val => { @@ -255,7 +255,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }); tablet.BindTo(tabletHandler.Tablet); - tablet.BindValueChanged(val => + tablet.BindValueChanged(val => Schedule(() => { Scheduler.AddOnce(updateVisibility); @@ -274,7 +274,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input sizeY.Default = sizeY.MaxValue = tab.Size.Y; areaSize.Default = new Vector2(sizeX.Default, sizeY.Default); - }, true); + }), true); } private void updateVisibility() From 4e9b25d8b0828039524974123f48d0215ef12ca2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 15:42:37 +0900 Subject: [PATCH 053/295] Move `MusicController` binds to `LoadComplete` for added thread safety --- osu.Game/Overlays/MusicController.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index da87336039..793b7e294f 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -58,12 +58,11 @@ namespace osu.Game.Overlays [Resolved] private RealmAccess realm { get; set; } - [BackgroundDependencyLoader] - private void load() + protected override void LoadComplete() { - // Todo: These binds really shouldn't be here, but are unlikely to cause any issues for now. - // They are placed here for now since some tests rely on setting the beatmap _and_ their hierarchies inside their load(), which runs before the MusicController's load(). - beatmap.BindValueChanged(beatmapChanged, true); + base.LoadComplete(); + + beatmap.BindValueChanged(b => changeBeatmap(b.NewValue), true); mods.BindValueChanged(_ => ResetTrackAdjustments(), true); } @@ -263,8 +262,6 @@ namespace osu.Game.Overlays private IQueryable getBeatmapSets() => realm.Realm.All().Where(s => !s.DeletePending); - private void beatmapChanged(ValueChangedEvent beatmap) => changeBeatmap(beatmap.NewValue); - private void changeBeatmap(WorkingBeatmap newWorking) { // This method can potentially be triggered multiple times as it is eagerly fired in next() / prev() to ensure correct execution order From 32d56fe3a959a40082094b3d9399e178039f8766 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 16:02:33 +0900 Subject: [PATCH 054/295] Use request cancellation and `IsDisposed` guard instead of more `Schedule` --- osu.Game/Online/Leaderboards/Leaderboard.cs | 13 +++++++++---- .../Select/Leaderboards/BeatmapLeaderboard.cs | 17 ++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 206c15afde..0f444a368a 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -185,10 +185,15 @@ namespace osu.Game.Online.Leaderboards if (scores != null) this.scores.AddRange(scores); - // Schedule needs to be non-delayed here for the weird logic in refetchScores to work. - // If it is removed, the placeholder will be incorrectly updated to "no scores" rather than "retrieving". - // This whole flow should be refactored in the future. - Scheduler.Add(applyNewScores, false); + // Non-delayed schedule may potentially run inline (due to IsMainThread check passing) after leaderboard is disposed. + // This is guarded against in BeatmapLeaderboard via web request cancellation, but let's be extra safe. + if (!IsDisposed) + { + // Schedule needs to be non-delayed here for the weird logic in refetchScores to work. + // If it is removed, the placeholder will be incorrectly updated to "no scores" rather than "retrieving". + // This whole flow should be refactored in the future. + Scheduler.Add(applyNewScores, false); + } void applyNewScores() { diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index ffb13dc458..50204db2bf 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -89,6 +89,8 @@ namespace osu.Game.Screens.Select.Leaderboards private IDisposable scoreSubscription; + private GetScoresRequest scoreRetrievalRequest; + [BackgroundDependencyLoader] private void load() { @@ -151,15 +153,14 @@ namespace osu.Game.Screens.Select.Leaderboards else if (filterMods) requestMods = mods.Value; - var req = new GetScoresRequest(fetchBeatmapInfo, fetchRuleset, Scope, requestMods); + scoreRetrievalRequest = new GetScoresRequest(fetchBeatmapInfo, fetchRuleset, Scope, requestMods); - // Schedule is required to avoid potential object disposed exception when LoadComponentAsync is eventually called. - req.Success += r => Schedule(() => SetScores( - scoreManager.OrderByTotalScore(r.Scores.Select(s => s.ToScoreInfo(rulesets, fetchBeatmapInfo))), - r.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo) - )); + scoreRetrievalRequest.Success += response => SetScores( + scoreManager.OrderByTotalScore(response.Scores.Select(s => s.ToScoreInfo(rulesets, fetchBeatmapInfo))), + response.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo) + ); - return req; + return scoreRetrievalRequest; } protected override LeaderboardScore CreateDrawableScore(ScoreInfo model, int index) => new LeaderboardScore(model, index, IsOnlineScope) @@ -219,7 +220,9 @@ namespace osu.Game.Screens.Select.Leaderboards protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + scoreSubscription?.Dispose(); + scoreRetrievalRequest?.Cancel(); } } } From a1297af4410beba1dc8d1c5a24e9165eeea01b06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 16:12:47 +0900 Subject: [PATCH 055/295] Apply NRT to base `Leaderboard` classes --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 13 ++++------ osu.Game/Online/Leaderboards/Leaderboard.cs | 24 ++++++++----------- .../Leaderboards/UserTopScoreContainer.cs | 10 ++++---- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 07da1790c8..1839821bb5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -36,10 +34,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; - private ScoreManager scoreManager; - - private RulesetStore rulesetStore; - private BeatmapManager beatmapManager; + private ScoreManager scoreManager = null!; + private RulesetStore rulesetStore = null!; + private BeatmapManager beatmapManager = null!; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -74,7 +71,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestLocalScoresDisplay() { - BeatmapInfo beatmapInfo = null; + BeatmapInfo beatmapInfo = null!; AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local); @@ -387,7 +384,7 @@ namespace osu.Game.Tests.Visual.SongSelect private class FailableLeaderboard : BeatmapLeaderboard { public new void SetErrorState(LeaderboardState state) => base.SetErrorState(state); - public new void SetScores(IEnumerable scores, ScoreInfo userScore = default) => base.SetScores(scores, userScore); + public new void SetScores(IEnumerable? scores, ScoreInfo? userScore = null) => base.SetScores(scores, userScore); } } } diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 0f444a368a..69b4e5b209 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -1,14 +1,11 @@ // 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.Diagnostics; using System.Linq; using System.Threading; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Development; @@ -54,23 +51,23 @@ namespace osu.Game.Online.Leaderboards private readonly Container placeholderContainer; private readonly UserTopScoreContainer userScoreContainer; - private FillFlowContainer scoreFlowContainer; + private FillFlowContainer? scoreFlowContainer; private readonly LoadingSpinner loading; - private CancellationTokenSource currentFetchCancellationSource; - private CancellationTokenSource currentScoresAsyncLoadCancellationSource; + private CancellationTokenSource? currentFetchCancellationSource; + private CancellationTokenSource? currentScoresAsyncLoadCancellationSource; - private APIRequest fetchScoresRequest; + private APIRequest? fetchScoresRequest; private LeaderboardState state; [Resolved(CanBeNull = true)] - private IAPIProvider api { get; set; } + private IAPIProvider? api { get; set; } private readonly IBindable apiState = new Bindable(); - private TScope scope; + private TScope scope = default!; public TScope Scope { @@ -179,7 +176,7 @@ namespace osu.Game.Online.Leaderboards /// /// The scores to display. /// The user top score, if any. - protected void SetScores(IEnumerable scores, TScoreInfo userScore = default) + protected void SetScores(IEnumerable? scores, TScoreInfo? userScore = default) { this.scores.Clear(); if (scores != null) @@ -213,8 +210,7 @@ namespace osu.Game.Online.Leaderboards /// /// /// An responsible for the fetch operation. This will be queued and performed automatically. - [CanBeNull] - protected abstract APIRequest FetchScores(CancellationToken cancellationToken); + protected abstract APIRequest? FetchScores(CancellationToken cancellationToken); protected abstract LeaderboardScore CreateDrawableScore(TScoreInfo model, int index); @@ -298,7 +294,7 @@ namespace osu.Game.Online.Leaderboards #region Placeholder handling - private Placeholder placeholder; + private Placeholder? placeholder; private void setState(LeaderboardState state) { @@ -325,7 +321,7 @@ namespace osu.Game.Online.Leaderboards placeholder.FadeInFromZero(fade_duration, Easing.OutQuint); } - private Placeholder getPlaceholderFor(LeaderboardState state) + private Placeholder? getPlaceholderFor(LeaderboardState state) { switch (state) { diff --git a/osu.Game/Online/Leaderboards/UserTopScoreContainer.cs b/osu.Game/Online/Leaderboards/UserTopScoreContainer.cs index 2d2d82821c..391e8804f0 100644 --- a/osu.Game/Online/Leaderboards/UserTopScoreContainer.cs +++ b/osu.Game/Online/Leaderboards/UserTopScoreContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Threading; using osu.Framework.Bindables; @@ -18,13 +16,15 @@ namespace osu.Game.Online.Leaderboards { private const int duration = 500; - public Bindable Score = new Bindable(); + public Bindable Score = new Bindable(); private readonly Container scoreContainer; private readonly Func createScoreDelegate; protected override bool StartHidden => true; + private CancellationTokenSource? loadScoreCancellation; + public UserTopScoreContainer(Func createScoreDelegate) { this.createScoreDelegate = createScoreDelegate; @@ -65,9 +65,7 @@ namespace osu.Game.Online.Leaderboards Score.BindValueChanged(onScoreChanged); } - private CancellationTokenSource loadScoreCancellation; - - private void onScoreChanged(ValueChangedEvent score) + private void onScoreChanged(ValueChangedEvent score) { var newScore = score.NewValue; From 5fc836d1f09cebf983313c9b91a5c252890c607a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 16:15:18 +0900 Subject: [PATCH 056/295] Apply NRT to `BeatmapLeaderboard` / `MatchLeaderboard` --- .../Match/Components/MatchLeaderboard.cs | 6 ++-- .../Select/Leaderboards/BeatmapLeaderboard.cs | 35 +++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs index 64644be965..ee5ee576d8 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -16,7 +14,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components public class MatchLeaderboard : Leaderboard { [Resolved(typeof(Room), nameof(Room.RoomID))] - private Bindable roomId { get; set; } + private Bindable roomId { get; set; } = null!; [BackgroundDependencyLoader] private void load() @@ -33,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected override bool IsOnlineScope => true; - protected override APIRequest FetchScores(CancellationToken cancellationToken) + protected override APIRequest? FetchScores(CancellationToken cancellationToken) { if (roomId.Value == null) return null; diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 50204db2bf..161d4847bf 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Diagnostics; @@ -25,11 +23,11 @@ namespace osu.Game.Screens.Select.Leaderboards { public class BeatmapLeaderboard : Leaderboard { - public Action ScoreSelected; + public Action? ScoreSelected; - private BeatmapInfo beatmapInfo; + private BeatmapInfo? beatmapInfo; - public BeatmapInfo BeatmapInfo + public BeatmapInfo? BeatmapInfo { get => beatmapInfo; set @@ -70,26 +68,26 @@ namespace osu.Game.Screens.Select.Leaderboards } [Resolved] - private ScoreManager scoreManager { get; set; } + private ScoreManager scoreManager { get; set; } = null!; [Resolved] - private IBindable ruleset { get; set; } + private IBindable ruleset { get; set; } = null!; [Resolved] - private IBindable> mods { get; set; } + private IBindable> mods { get; set; } = null!; [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; [Resolved] - private RulesetStore rulesets { get; set; } + private RulesetStore rulesets { get; set; } = null!; [Resolved] - private RealmAccess realm { get; set; } + private RealmAccess realm { get; set; } = null!; - private IDisposable scoreSubscription; + private IDisposable? scoreSubscription; - private GetScoresRequest scoreRetrievalRequest; + private GetScoresRequest? scoreRetrievalRequest; [BackgroundDependencyLoader] private void load() @@ -104,10 +102,9 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; - protected override APIRequest FetchScores(CancellationToken cancellationToken) + protected override APIRequest? FetchScores(CancellationToken cancellationToken) { var fetchBeatmapInfo = BeatmapInfo; - var fetchRuleset = ruleset.Value ?? fetchBeatmapInfo.Ruleset; if (fetchBeatmapInfo == null) { @@ -115,13 +112,15 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } + var fetchRuleset = ruleset.Value ?? fetchBeatmapInfo.Ruleset; + if (Scope == BeatmapLeaderboardScope.Local) { subscribeToLocalScores(fetchBeatmapInfo, cancellationToken); return null; } - if (api?.IsLoggedIn != true) + if (!api.IsLoggedIn) { SetErrorState(LeaderboardState.NotLoggedIn); return null; @@ -145,7 +144,7 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - IReadOnlyList requestMods = null; + IReadOnlyList? requestMods = null; if (filterMods && !mods.Value.Any()) // add nomod for the request @@ -186,7 +185,7 @@ namespace osu.Game.Screens.Select.Leaderboards + $" AND {nameof(ScoreInfo.DeletePending)} == false" , beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); - void localScoresChanged(IRealmCollection sender, ChangeSet changes, Exception exception) + void localScoresChanged(IRealmCollection sender, ChangeSet? changes, Exception exception) { if (cancellationToken.IsCancellationRequested) return; From 20da1051d9ab2e217f8895d4020683445618aa27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 16:21:23 +0900 Subject: [PATCH 057/295] Apply NRT to `GameplayLeaderboard` hierarchy Just some cleanup work that I wanted to do as part of my last changes. --- .../Spectate/MultiSpectatorLeaderboard.cs | 2 -- .../HUD/MultiplayerGameplayLeaderboard.cs | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs index c7af87a91d..4e9ab07e4c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Timing; using osu.Game.Online.Multiplayer; diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 56756249b3..4201b3f4c9 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -12,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics; @@ -34,19 +33,20 @@ namespace osu.Game.Screens.Play.HUD public readonly SortedDictionary TeamScores = new SortedDictionary(); [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; [Resolved] - private SpectatorClient spectatorClient { get; set; } + private SpectatorClient spectatorClient { get; set; } = null!; [Resolved] - private MultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } = null!; [Resolved] - private UserLookupCache userLookupCache { get; set; } + private UserLookupCache userLookupCache { get; set; } = null!; + + private Bindable scoringMode = null!; private readonly MultiplayerRoomUser[] playingUsers; - private Bindable scoringMode; private readonly IBindableList playingUserIds = new BindableList(); @@ -126,14 +126,17 @@ namespace osu.Game.Screens.Play.HUD playingUserIds.BindCollectionChanged(playingUsersChanged); } - protected override GameplayLeaderboardScore CreateLeaderboardScoreDrawable(IUser user, bool isTracked) + protected override GameplayLeaderboardScore CreateLeaderboardScoreDrawable(IUser? user, bool isTracked) { var leaderboardScore = base.CreateLeaderboardScoreDrawable(user, isTracked); - if (UserScores[user.OnlineID].Team is int team) + if (user != null) { - leaderboardScore.BackgroundColour = getTeamColour(team).Lighten(1.2f); - leaderboardScore.TextColour = Color4.White; + if (UserScores[user.OnlineID].Team is int team) + { + leaderboardScore.BackgroundColour = getTeamColour(team).Lighten(1.2f); + leaderboardScore.TextColour = Color4.White; + } } return leaderboardScore; @@ -189,7 +192,7 @@ namespace osu.Game.Screens.Play.HUD { base.Dispose(isDisposing); - if (spectatorClient != null) + if (spectatorClient.IsNotNull()) { foreach (var user in playingUsers) spectatorClient.StopWatchingUser(user.UserID); From 0f18bef3f366eff9de289c0eb5bad5a4657867d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Sep 2022 17:59:39 +0900 Subject: [PATCH 058/295] Fix incorrect test assumptions --- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index db380cfdb7..c1a9768cf0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -163,7 +163,7 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.PressButton(MouseButton.Left); }); - AddUntilStep("wait for fetch", () => leaderboard.Scores != null); + AddUntilStep("wait for fetch", () => leaderboard.Scores.Any()); AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != scoreBeingDeleted.OnlineID)); // "Clean up" @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestDeleteViaDatabase() { AddStep("delete top score", () => scoreManager.Delete(importedScores[0])); - AddUntilStep("wait for fetch", () => leaderboard.Scores != null); + AddUntilStep("wait for fetch", () => leaderboard.Scores.Any()); AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != importedScores[0].OnlineID)); } } From 6e1edc4d8d81ada51e958d5920846790b97cedf3 Mon Sep 17 00:00:00 2001 From: abstrakt Date: Mon, 26 Sep 2022 14:06:35 +0200 Subject: [PATCH 059/295] Use the `StackedEndPosition` to determine the jump distance in the `FlashlightEvaluator`. Signed-off-by: abstrakt --- .../Difficulty/Evaluators/FlashlightEvaluator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index 2ba856d014..dabbfcd2fb 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators if (!(currentObj.BaseObject is Spinner)) { - double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.EndPosition).Length; + double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length; cumulativeStrainTime += lastObj.StrainTime; From f81107eb5ac7dacb58c2b0955a975e0113eaf5e2 Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 20:07:43 +0800 Subject: [PATCH 060/295] add gameplay leaderboard config --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 54c545e367..8d1de971b3 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -131,6 +131,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); + SetDefault(OsuSetting.GameplayLeaderboard, true); SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true); SetDefault(OsuSetting.FloatingComments, false); @@ -294,6 +295,7 @@ namespace osu.Game.Configuration LightenDuringBreaks, ShowStoryboard, KeyOverlay, + GameplayLeaderboard, PositionalHitsounds, PositionalHitsoundsLevel, AlwaysPlayFirstComboBreak, From bed25ff60e92b87d212a15281d65556e555e587c Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 20:08:26 +0800 Subject: [PATCH 061/295] add gameplay leaderboard config string --- osu.Game/Localisation/GameplaySettingsStrings.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs index 13cfcc3a19..40f39d927d 100644 --- a/osu.Game/Localisation/GameplaySettingsStrings.cs +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -79,6 +79,11 @@ namespace osu.Game.Localisation /// public static LocalisableString AlwaysShowKeyOverlay => new TranslatableString(getKey(@"key_overlay"), @"Always show key overlay"); + /// + /// "Always show gameplay leaderboard" + /// + public static LocalisableString AlwaysShowGameplayLeaderboard => new TranslatableString(getKey(@"gameplay_leaderboard"), @"Always show gameplay leaderboard"); + /// /// "Always play first combo break sound" /// From dc051a8b7992ebf6f7a7b8f08da6b16ea8b97ee8 Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 20:08:58 +0800 Subject: [PATCH 062/295] add gameplay leaderboard config --- osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index 0893af7d3e..12534c51a8 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -38,6 +38,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay Current = config.GetBindable(OsuSetting.KeyOverlay), Keywords = new[] { "counter" }, }, + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.AlwaysShowGameplayLeaderboard, + Current = config.GetBindable(OsuSetting.GameplayLeaderboard), + Keywords = new[] { "leaderboard", "score" }, + }, }; } } From ee4d1b2d58cbb2d3c2fa51f65d88eea438f7e070 Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 20:13:07 +0800 Subject: [PATCH 063/295] add config visibility --- .../Screens/Play/HUD/GameplayLeaderboard.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 2d816fbd55..b618ce907d 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -3,12 +3,14 @@ using System; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Users; using osuTK; @@ -18,6 +20,10 @@ namespace osu.Game.Screens.Play.HUD { public abstract class GameplayLeaderboard : CompositeDrawable { + + private const int duration = 100; + + private readonly Bindable configVisibility = new Bindable(); private readonly Cached sorting = new Cached(); public Bindable Expanded = new Bindable(); @@ -57,11 +63,19 @@ namespace osu.Game.Screens.Play.HUD }; } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility); + } + protected override void LoadComplete() { base.LoadComplete(); Scheduler.AddDelayed(sort, 1000, true); + + configVisibility.BindValueChanged(_ => updateVisibility(), true); } /// @@ -193,5 +207,8 @@ namespace osu.Game.Screens.Play.HUD public override bool HandlePositionalInput => false; public override bool HandleNonPositionalInput => false; } + + private void updateVisibility() => + Flow.FadeTo(configVisibility.Value ? 1 : 0, duration); } } From 5c132aadeb4fb60bd4dc0a50e3a9d5872643b0f1 Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 20:15:17 +0800 Subject: [PATCH 064/295] add visual test --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 72656c29b1..eb5846669f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -5,12 +5,14 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.PolygonExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Play.HUD; using osuTK; @@ -24,6 +26,8 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly BindableDouble playerScore = new BindableDouble(); + private Bindable configVisibility = new Bindable(); + public TestSceneGameplayLeaderboard() { AddStep("toggle expanded", () => @@ -35,6 +39,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility); + } + [Test] public void TestLayoutWithManyScores() { @@ -129,6 +139,21 @@ namespace osu.Game.Tests.Visual.Gameplay => AddAssert($"leaderboard height is {panelCount} panels high", () => leaderboard.DrawHeight == (GameplayLeaderboardScore.PANEL_HEIGHT + leaderboard.Spacing) * panelCount); } + [Test] + public void TestVisibility() + { + createLeaderboard(); + addLocalPlayer(); + + AddStep("set visible true", () => configVisibility.Value = true); + AddWaitStep("wait", 1); + AddAssert("is leaderboard fully visible", () => leaderboard.FlowAlpha == 1); + + AddStep("set visible false", () => configVisibility.Value = false); + AddWaitStep("wait", 1); + AddAssert("is leaderboard fully invisible", () => leaderboard.FlowAlpha == 0); + } + private void addLocalPlayer() { AddStep("add local player", () => @@ -163,6 +188,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public float Spacing => Flow.Spacing.Y; + public float FlowAlpha => Flow.Alpha; + public bool CheckPositionByUsername(string username, int? expectedPosition) { var scoreItem = Flow.FirstOrDefault(i => i.User?.Username == username); From 4295d9c169b5b1453162131909c6f58c95ff7cdb Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 21:11:14 +0800 Subject: [PATCH 065/295] revert --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 27 ------------------- .../Screens/Play/HUD/GameplayLeaderboard.cs | 17 ------------ 2 files changed, 44 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index eb5846669f..72656c29b1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -5,14 +5,12 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.PolygonExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Play.HUD; using osuTK; @@ -26,8 +24,6 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly BindableDouble playerScore = new BindableDouble(); - private Bindable configVisibility = new Bindable(); - public TestSceneGameplayLeaderboard() { AddStep("toggle expanded", () => @@ -39,12 +35,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility); - } - [Test] public void TestLayoutWithManyScores() { @@ -139,21 +129,6 @@ namespace osu.Game.Tests.Visual.Gameplay => AddAssert($"leaderboard height is {panelCount} panels high", () => leaderboard.DrawHeight == (GameplayLeaderboardScore.PANEL_HEIGHT + leaderboard.Spacing) * panelCount); } - [Test] - public void TestVisibility() - { - createLeaderboard(); - addLocalPlayer(); - - AddStep("set visible true", () => configVisibility.Value = true); - AddWaitStep("wait", 1); - AddAssert("is leaderboard fully visible", () => leaderboard.FlowAlpha == 1); - - AddStep("set visible false", () => configVisibility.Value = false); - AddWaitStep("wait", 1); - AddAssert("is leaderboard fully invisible", () => leaderboard.FlowAlpha == 0); - } - private void addLocalPlayer() { AddStep("add local player", () => @@ -188,8 +163,6 @@ namespace osu.Game.Tests.Visual.Gameplay { public float Spacing => Flow.Spacing.Y; - public float FlowAlpha => Flow.Alpha; - public bool CheckPositionByUsername(string username, int? expectedPosition) { var scoreItem = Flow.FirstOrDefault(i => i.User?.Username == username); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index b618ce907d..2d816fbd55 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -3,14 +3,12 @@ using System; using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Users; using osuTK; @@ -20,10 +18,6 @@ namespace osu.Game.Screens.Play.HUD { public abstract class GameplayLeaderboard : CompositeDrawable { - - private const int duration = 100; - - private readonly Bindable configVisibility = new Bindable(); private readonly Cached sorting = new Cached(); public Bindable Expanded = new Bindable(); @@ -63,19 +57,11 @@ namespace osu.Game.Screens.Play.HUD }; } - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility); - } - protected override void LoadComplete() { base.LoadComplete(); Scheduler.AddDelayed(sort, 1000, true); - - configVisibility.BindValueChanged(_ => updateVisibility(), true); } /// @@ -207,8 +193,5 @@ namespace osu.Game.Screens.Play.HUD public override bool HandlePositionalInput => false; public override bool HandleNonPositionalInput => false; } - - private void updateVisibility() => - Flow.FadeTo(configVisibility.Value ? 1 : 0, duration); } } From 5d18001d7542159c2959ea75499b4ca5b6da8621 Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 21:11:38 +0800 Subject: [PATCH 066/295] move config --- .../Screens/Play/HUD/SoloGameplayLeaderboard.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs index eff7870d89..fb7e2ff00f 100644 --- a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs @@ -5,6 +5,8 @@ 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.Scoring; using osu.Game.Scoring; using osu.Game.Users; @@ -13,6 +15,9 @@ namespace osu.Game.Screens.Play.HUD { public class SoloGameplayLeaderboard : GameplayLeaderboard { + private const int duration = 100; + + private readonly Bindable configVisibility = new Bindable(); private readonly IUser trackingUser; public readonly IBindableList Scores = new BindableList(); @@ -31,10 +36,18 @@ namespace osu.Game.Screens.Play.HUD this.trackingUser = trackingUser; } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility); + } + protected override void LoadComplete() { base.LoadComplete(); Scores.BindCollectionChanged((_, _) => Scheduler.AddOnce(showScores), true); + + configVisibility.BindValueChanged(_ => updateVisibility(), true); } private void showScores() @@ -69,5 +82,8 @@ namespace osu.Game.Screens.Play.HUD // Local score should always show lower than any existing scores in cases of ties. local.DisplayOrder.Value = long.MaxValue; } + + private void updateVisibility() => + Flow.FadeTo(configVisibility.Value ? 1 : 0, duration); } } From 1fab1db145843644699095a4ab319d832c84e5ae Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 21:11:48 +0800 Subject: [PATCH 067/295] move test --- .../TestSceneSoloGameplayLeaderboard.cs | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs index ac73e88468..cb48275e21 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs @@ -9,11 +9,13 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Gameplay { @@ -24,6 +26,16 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly BindableList scores = new BindableList(); + private Bindable configVisibility = new Bindable(); + + private TestSoloGameplayLeaderboard? testSoloGameplayLeaderboard; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility); + } + [SetUpSteps] public void SetUpSteps() { @@ -37,7 +49,7 @@ namespace osu.Game.Tests.Visual.Gameplay Id = 2, }; - Child = new SoloGameplayLeaderboard(trackingUser) + Child = testSoloGameplayLeaderboard = new TestSoloGameplayLeaderboard(trackingUser) { Scores = { BindTarget = scores }, Anchor = Anchor.Centre, @@ -57,6 +69,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddSliderStep("combo", 0, 1000, 0, v => scoreProcessor.Combo.Value = v); } + [Test] + public void TestVisibility() + { + AddStep("set visible true", () => configVisibility.Value = true); + AddWaitStep("wait", 1); + AddAssert("is leaderboard fully visible", () => testSoloGameplayLeaderboard?.FlowAlpha == 1); + + AddStep("set visible false", () => configVisibility.Value = false); + AddWaitStep("wait", 1); + AddAssert("is leaderboard fully invisible", () => testSoloGameplayLeaderboard?.FlowAlpha == 0); + } + private static List createSampleScores() { return new[] @@ -68,5 +92,13 @@ namespace osu.Game.Tests.Visual.Gameplay new ScoreInfo { User = new APIUser { Username = @"Susko3" }, TotalScore = RNG.Next(500000, 1000000) }, }.Concat(Enumerable.Range(0, 50).Select(i => new ScoreInfo { User = new APIUser { Username = $"User {i + 1}" }, TotalScore = 1000000 - i * 10000 })).ToList(); } + + private class TestSoloGameplayLeaderboard : SoloGameplayLeaderboard + { + public float FlowAlpha => Flow.Alpha; + public TestSoloGameplayLeaderboard(IUser trackingUser) + : base(trackingUser) + { } + } } } From 214a1c2d7fede26719017d7e8dcf73838661d02a Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Mon, 26 Sep 2022 21:26:13 +0800 Subject: [PATCH 068/295] code factor --- .../Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs index cb48275e21..128e8b6c8a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly BindableList scores = new BindableList(); - private Bindable configVisibility = new Bindable(); + private readonly Bindable configVisibility = new Bindable(); private TestSoloGameplayLeaderboard? testSoloGameplayLeaderboard; @@ -96,9 +96,11 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestSoloGameplayLeaderboard : SoloGameplayLeaderboard { public float FlowAlpha => Flow.Alpha; + public TestSoloGameplayLeaderboard(IUser trackingUser) - : base(trackingUser) - { } + : base(trackingUser) + { + } } } } From 8aff856c793d8078d0217a6a0bf0cab837f37728 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 27 Sep 2022 05:18:13 +0300 Subject: [PATCH 069/295] Fix `SkinnableTestScene` sizing logic potentially failing to work --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index a8193701ef..f8f15e2729 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -140,6 +140,7 @@ namespace osu.Game.Tests.Visual { c.RelativeSizeAxes = Axes.None; c.AutoSizeAxes = Axes.None; + c.Size = Vector2.Zero; c.RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None; c.AutoSizeAxes = autoSize ? Axes.Both : Axes.None; From de6709d12c5cd5f2d7d522d1f074095fe223e6f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 12:46:15 +0900 Subject: [PATCH 070/295] Fix `rank_history` serialisation order dependence ```csharp [network] 2022-09-26 18:18:39 [verbose]: Processing response from https://dev.ppy.sh/api/v2/me/ failed with Newtonsoft.Json.JsonSerializationException: Error setting value to 'rankHistory' on 'osu.Game.Online.API.Requests.Responses.APIUser'. [network] 2022-09-26 18:18:39 [verbose]: ---> System.NullReferenceException: Object reference not set to an instance of an object. [network] 2022-09-26 18:18:39 [verbose]: at osu.Game.Online.API.Requests.Responses.APIUser.set_rankHistory(APIRankHistory value) in /tmp/osu/osu.Game/Online/API/Requests/Responses/APIUser.cs:line 231 ``` --- osu.Game/Online/API/Requests/Responses/APIUser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIUser.cs b/osu.Game/Online/API/Requests/Responses/APIUser.cs index 5f843e9a7b..d3ddcffaf5 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUser.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUser.cs @@ -228,7 +228,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"rank_history")] private APIRankHistory rankHistory { - set => statistics.RankHistory = value; + set => Statistics.RankHistory = value; } [JsonProperty("badges")] From bac3108aea03fb72b9ccc146b6cfb7353cf8f63a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 14:29:59 +0900 Subject: [PATCH 071/295] Remove unnecessary keywords --- osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index 12534c51a8..88a27840d8 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -42,7 +42,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { LabelText = GameplaySettingsStrings.AlwaysShowGameplayLeaderboard, Current = config.GetBindable(OsuSetting.GameplayLeaderboard), - Keywords = new[] { "leaderboard", "score" }, }, }; } From 320f134634fa3095c8522abc0b228c22f2671d92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 14:43:08 +0900 Subject: [PATCH 072/295] Use same logic as `KeyCounterDisplay` --- osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs | 9 ++++++++- osu.Game/Screens/Play/ReplayPlayer.cs | 1 + osu.Game/Screens/Play/SoloPlayer.cs | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs index fb7e2ff00f..471aaf9a6d 100644 --- a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs @@ -31,6 +31,12 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private ScoreManager scoreManager { get; set; } = null!; + /// + /// Whether the leaderboard should be visible regardless of the configuration value. + /// This is true by default, but can be changed. + /// + public readonly Bindable AlwaysVisible = new Bindable(true); + public SoloGameplayLeaderboard(IUser trackingUser) { this.trackingUser = trackingUser; @@ -47,6 +53,7 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); Scores.BindCollectionChanged((_, _) => Scheduler.AddOnce(showScores), true); + AlwaysVisible.BindValueChanged(_ => updateVisibility()); configVisibility.BindValueChanged(_ => updateVisibility(), true); } @@ -84,6 +91,6 @@ namespace osu.Game.Screens.Play.HUD } private void updateVisibility() => - Flow.FadeTo(configVisibility.Value ? 1 : 0, duration); + this.FadeTo(AlwaysVisible.Value || configVisibility.Value ? 1 : 0, duration); } } diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 163b9c593f..5382e283e0 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -62,6 +62,7 @@ namespace osu.Game.Screens.Play protected override GameplayLeaderboard CreateGameplayLeaderboard() => new SoloGameplayLeaderboard(Score.ScoreInfo.User) { + AlwaysVisible = { Value = true }, Scores = { BindTarget = LeaderboardScores } }; diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index d7730737d6..ee19391b89 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -48,6 +48,7 @@ namespace osu.Game.Screens.Play protected override GameplayLeaderboard CreateGameplayLeaderboard() => new SoloGameplayLeaderboard(Score.ScoreInfo.User) { + AlwaysVisible = { Value = false }, Scores = { BindTarget = LeaderboardScores } }; From 4260ace1a6d24ff2a5282a89d93646b5a75a508a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 14:46:35 +0900 Subject: [PATCH 073/295] Fix test coverage --- .../TestSceneSoloGameplayLeaderboard.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs index 128e8b6c8a..8e438e98b6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly Bindable configVisibility = new Bindable(); - private TestSoloGameplayLeaderboard? testSoloGameplayLeaderboard; + private SoloGameplayLeaderboard leaderboard = null!; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -49,11 +49,12 @@ namespace osu.Game.Tests.Visual.Gameplay Id = 2, }; - Child = testSoloGameplayLeaderboard = new TestSoloGameplayLeaderboard(trackingUser) + Child = leaderboard = new SoloGameplayLeaderboard(trackingUser) { Scores = { BindTarget = scores }, Anchor = Anchor.Centre, Origin = Anchor.Centre, + AlwaysVisible = { Value = false }, Expanded = { Value = true }, }; }); @@ -72,13 +73,17 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestVisibility() { - AddStep("set visible true", () => configVisibility.Value = true); - AddWaitStep("wait", 1); - AddAssert("is leaderboard fully visible", () => testSoloGameplayLeaderboard?.FlowAlpha == 1); + AddStep("set config visible true", () => configVisibility.Value = true); + AddUntilStep("leaderboard visible", () => leaderboard.Alpha == 1); - AddStep("set visible false", () => configVisibility.Value = false); - AddWaitStep("wait", 1); - AddAssert("is leaderboard fully invisible", () => testSoloGameplayLeaderboard?.FlowAlpha == 0); + AddStep("set config visible false", () => configVisibility.Value = false); + AddUntilStep("leaderboard not visible", () => leaderboard.Alpha == 0); + + AddStep("set always visible", () => leaderboard.AlwaysVisible.Value = true); + AddUntilStep("leaderboard visible", () => leaderboard.Alpha == 1); + + AddStep("set config visible true", () => configVisibility.Value = true); + AddAssert("leaderboard still visible", () => leaderboard.Alpha == 1); } private static List createSampleScores() @@ -92,15 +97,5 @@ namespace osu.Game.Tests.Visual.Gameplay new ScoreInfo { User = new APIUser { Username = @"Susko3" }, TotalScore = RNG.Next(500000, 1000000) }, }.Concat(Enumerable.Range(0, 50).Select(i => new ScoreInfo { User = new APIUser { Username = $"User {i + 1}" }, TotalScore = 1000000 - i * 10000 })).ToList(); } - - private class TestSoloGameplayLeaderboard : SoloGameplayLeaderboard - { - public float FlowAlpha => Flow.Alpha; - - public TestSoloGameplayLeaderboard(IUser trackingUser) - : base(trackingUser) - { - } - } } } From 87a1e05641d596025c73f3a572560990d391eb69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 15:06:05 +0900 Subject: [PATCH 074/295] Remove unused using statement --- .../Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs index 8e438e98b6..6b52e934b7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; -using osu.Game.Users; namespace osu.Game.Tests.Visual.Gameplay { From 9dc0eb7fd02a81aebabb36a75c4573be545bf666 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:04:56 +0900 Subject: [PATCH 075/295] Remove unused static --- .../Edit/Compose/Components/Timeline/TimelineTickDisplay.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 076ce224f0..fce73d908f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -34,8 +33,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private OsuColour colours { get; set; } - private static readonly int highest_divisor = BindableBeatDivisor.PREDEFINED_DIVISORS.Last(); - public TimelineTickDisplay() { RelativeSizeAxes = Axes.Both; From ccae721af2577dca7141b534204b3c552ec46cc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:05:10 +0900 Subject: [PATCH 076/295] Ensure `ZoomableScrollContainer` content isn't shown until zoom is set --- .../Timeline/ZoomableScrollContainer.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs index 0fb59a8a1f..839b2b5bad 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs @@ -56,7 +56,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected ZoomableScrollContainer() : base(Direction.Horizontal) { - base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y }); + base.Content.Add(zoomedContent = new Container + { + RelativeSizeAxes = Axes.Y, + // We must hide content until SetupZoom is called. + // If not, a child component that relies on its DrawWidth (via RelativeSizeAxes) may see a very incorrect value + // momentarily, as noticed in the TimelineTickDisplay, which would render thousands of ticks incorrectly. + Alpha = 0, + }); AddLayout(zoomedContentWidthCache); } @@ -94,6 +101,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline maxZoom = maximum; CurrentZoom = zoomTarget = initial; isZoomSetUp = true; + + zoomedContent.Show(); } /// @@ -118,9 +127,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline CurrentZoom = zoomTarget = newZoom; } - protected override void Update() + protected override void UpdateAfterChildren() { - base.Update(); + base.UpdateAfterChildren(); if (!zoomedContentWidthCache.IsValid) updateZoomedContentWidth(); From e227519b093b4b2fb48dab6f5eabdf5364e8fa0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:18:45 +0900 Subject: [PATCH 077/295] Don't attempt to render timeline ticks until `DrawWidth` is above zero --- .../Timeline/TimelineTickDisplay.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index fce73d908f..9018c95a47 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -77,20 +77,19 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { base.Update(); - if (timeline != null) + if (timeline == null || !(DrawWidth > 0)) return; + + (float, float) newRange = ( + (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X, + (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X); + + if (visibleRange != newRange) { - var newRange = ( - (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X, - (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X); + visibleRange = newRange; - if (visibleRange != newRange) - { - visibleRange = newRange; - - // actual regeneration only needs to occur if we've passed one of the known next min/max tick boundaries. - if (nextMinTick == null || nextMaxTick == null || (visibleRange.min < nextMinTick || visibleRange.max > nextMaxTick)) - tickCache.Invalidate(); - } + // actual regeneration only needs to occur if we've passed one of the known next min/max tick boundaries. + if (nextMinTick == null || nextMaxTick == null || (visibleRange.min < nextMinTick || visibleRange.max > nextMaxTick)) + tickCache.Invalidate(); } if (!tickCache.IsValid) From a9088d9231042d4dccee5a95a96551b66242224c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:11:36 +0900 Subject: [PATCH 078/295] Add assert/log output when too many ticks are being displayed --- .../Components/Timeline/TimelineTickDisplay.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 9018c95a47..251306ee25 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -4,10 +4,12 @@ #nullable disable using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Graphics; +using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -147,6 +149,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } + if (Children.Count > 512) + { + // There should always be a sanely small number of ticks rendered. + // If this assertion triggers, either the zoom logic is broken or a beatmap is + // probably doing weird things... + // + // Let's hope the latter never happens. + // If it does, we can choose to either fix it or ignore it as an outlier. + string message = $"Timeline is rendering many ticks ({Children.Count})"; + + Logger.Log(message); + Debug.Fail(message); + } + int usedDrawables = drawableIndex; // save a few drawables beyond the currently used for edge cases. From 0296685c74bbc47cebdc9d7c09b91bc10e10ff6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:25:24 +0900 Subject: [PATCH 079/295] Start key counter / gameplay leaderboard hidden to avoid initial fade out --- osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs | 3 +++ osu.Game/Screens/Play/KeyCounterDisplay.cs | 1 + 2 files changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs index 471aaf9a6d..53981b9edc 100644 --- a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs @@ -53,6 +53,9 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); Scores.BindCollectionChanged((_, _) => Scheduler.AddOnce(showScores), true); + // Alpha will be updated via `updateVisibility` below. + Alpha = 0; + AlwaysVisible.BindValueChanged(_ => updateVisibility()); configVisibility.BindValueChanged(_ => updateVisibility(), true); } diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index b6094726c0..1b726b0f7b 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -39,6 +39,7 @@ namespace osu.Game.Screens.Play { Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, + Alpha = 0, }; } From c61f5403ab03378cce3013fa40f90714bd26083e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:32:30 +0900 Subject: [PATCH 080/295] Increase delay back slightly on notifications posting after disabled period --- osu.Game/Overlays/NotificationOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index fad8afd371..8e70f539a5 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -113,7 +113,7 @@ namespace osu.Game.Overlays if (enabled) // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. - notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 100); + notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 250); else processingPosts = false; } From 461ba64bfafa7c8a629f54e1c0fb4199a2a1790c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:32:53 +0900 Subject: [PATCH 081/295] Flush toast tray on entering a no-notification section --- osu.Game/Overlays/NotificationOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 8e70f539a5..36548c893c 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -115,7 +115,10 @@ namespace osu.Game.Overlays // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 250); else + { processingPosts = false; + toastTray.FlushAllToasts(); + } } protected override void LoadComplete() From 8b722f88172c20ff806d00a645048b7e9babd25d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:41:55 +0900 Subject: [PATCH 082/295] Change default skin for new installations to "Argon" Closes #20459. --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 54c545e367..c1d87915ce 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -39,7 +39,7 @@ namespace osu.Game.Configuration { // UI/selection defaults SetDefault(OsuSetting.Ruleset, string.Empty); - SetDefault(OsuSetting.Skin, SkinInfo.TRIANGLES_SKIN.ToString()); + SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString()); SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details); SetDefault(OsuSetting.BeatmapDetailModsFilter, false); From 3ece7205eda7ea1207375f2df4b38bb58918abcc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 17:52:29 +0900 Subject: [PATCH 083/295] Fix potential crash when losing network connectivity at online play screen Addresses #20448. --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 61ea7d68ee..7e5d90bd4f 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -105,7 +105,8 @@ namespace osu.Game.Screens.OnlinePlay while (this.IsCurrentScreen()) this.Exit(); } - else + // Also handle the case where a child screen is current (ie. gameplay). + else if (this.GetChildScreen() != null) { this.MakeCurrent(); Schedule(forcefullyExit); From 58217b68396d00d16b50d6e454320c8795af25ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 20:09:21 +0900 Subject: [PATCH 084/295] Fix weird conditional --- .../Edit/Compose/Components/Timeline/TimelineTickDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 251306ee25..c1c9b2493b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { base.Update(); - if (timeline == null || !(DrawWidth > 0)) return; + if (timeline == null || DrawWidth <= 0) return; (float, float) newRange = ( (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X, From da8d94c4b46cf891eef362cb0085e8480998b629 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 27 Sep 2022 20:24:44 +0900 Subject: [PATCH 085/295] Fix test failures due to scheduled operations --- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 15138b18b0..bbdfed0a00 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -54,6 +54,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { private const float disabled_alpha = 0.2f; + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public Action? SettingsApplied; public OsuTextBox NameField = null!; From 1811647e34c7c663b1d94722a02a26cae38a06d5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 27 Sep 2022 20:30:41 +0900 Subject: [PATCH 086/295] Make room requests handler handle GetBeatmapRequest --- .../Online/API/Requests/GetBeatmapRequest.cs | 22 +++++---- .../OnlinePlay/TestRoomRequestsHandler.cs | 45 ++++++++++++------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapRequest.cs index efecc0fc25..3383d21dfc 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapRequest.cs @@ -9,27 +9,25 @@ namespace osu.Game.Online.API.Requests { public class GetBeatmapRequest : APIRequest { - private readonly IBeatmapInfo beatmapInfo; - - private readonly string filename; + public readonly IBeatmapInfo BeatmapInfo; + public readonly string Filename; public GetBeatmapRequest(IBeatmapInfo beatmapInfo) { - this.beatmapInfo = beatmapInfo; - - filename = (beatmapInfo as BeatmapInfo)?.Path ?? string.Empty; + BeatmapInfo = beatmapInfo; + Filename = (beatmapInfo as BeatmapInfo)?.Path ?? string.Empty; } protected override WebRequest CreateWebRequest() { var request = base.CreateWebRequest(); - if (beatmapInfo.OnlineID > 0) - request.AddParameter(@"id", beatmapInfo.OnlineID.ToString()); - if (!string.IsNullOrEmpty(beatmapInfo.MD5Hash)) - request.AddParameter(@"checksum", beatmapInfo.MD5Hash); - if (!string.IsNullOrEmpty(filename)) - request.AddParameter(@"filename", filename); + if (BeatmapInfo.OnlineID > 0) + request.AddParameter(@"id", BeatmapInfo.OnlineID.ToString()); + if (!string.IsNullOrEmpty(BeatmapInfo.MD5Hash)) + request.AddParameter(@"checksum", BeatmapInfo.MD5Hash); + if (!string.IsNullOrEmpty(Filename)) + request.AddParameter(@"filename", Filename); return request; } diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index fa7ade2c07..ef4539ba56 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -135,25 +135,15 @@ namespace osu.Game.Tests.Visual.OnlinePlay }); return true; + case GetBeatmapRequest getBeatmapRequest: + { + getBeatmapRequest.TriggerSuccess(createResponseBeatmaps(getBeatmapRequest.BeatmapInfo.OnlineID).Single()); + return true; + } + case GetBeatmapsRequest getBeatmapsRequest: { - var result = new List(); - - foreach (int id in getBeatmapsRequest.BeatmapIds) - { - var baseBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == id); - - if (baseBeatmap == null) - { - baseBeatmap = new TestBeatmap(new RulesetInfo { OnlineID = 0 }).BeatmapInfo; - baseBeatmap.OnlineID = id; - baseBeatmap.BeatmapSet!.OnlineID = id; - } - - result.Add(OsuTestScene.CreateAPIBeatmap(baseBeatmap)); - } - - getBeatmapsRequest.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = result }); + getBeatmapsRequest.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = createResponseBeatmaps(getBeatmapsRequest.BeatmapIds.ToArray()) }); return true; } @@ -175,6 +165,27 @@ namespace osu.Game.Tests.Visual.OnlinePlay } } + List createResponseBeatmaps(params int[] beatmapIds) + { + var result = new List(); + + foreach (int id in beatmapIds) + { + var baseBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == id); + + if (baseBeatmap == null) + { + baseBeatmap = new TestBeatmap(new RulesetInfo { OnlineID = 0 }).BeatmapInfo; + baseBeatmap.OnlineID = id; + baseBeatmap.BeatmapSet!.OnlineID = id; + } + + result.Add(OsuTestScene.CreateAPIBeatmap(baseBeatmap)); + } + + return result; + } + return false; } From ed0752a5f12d29de2ec0e57e7cd9ae25ee0ef729 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 21:55:03 +0900 Subject: [PATCH 087/295] Update test assumptions --- osu.Game.Tests/Visual/Navigation/TestSceneEditDefaultSkin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneEditDefaultSkin.cs b/osu.Game.Tests/Visual/Navigation/TestSceneEditDefaultSkin.cs index 3757c49f18..010ed23c9b 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneEditDefaultSkin.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneEditDefaultSkin.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestEditDefaultSkin() { - AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.TRIANGLES_SKIN); + AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.ARGON_SKIN); AddStep("open settings", () => { Game.Settings.Show(); }); @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("open skin editor", () => skinEditor.Show()); // Until step required as the skin editor may take time to load (and an extra scheduled frame for the mutable part). - AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.TRIANGLES_SKIN); + AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.ARGON_SKIN); AddAssert("is not protected", () => skinManager.CurrentSkinInfo.Value.PerformRead(s => !s.Protected)); AddUntilStep("export button enabled", () => Game.Settings.ChildrenOfType().SingleOrDefault()?.Enabled.Value == true); From f3e25eacadc046c96669dab7e5f430b068d851ce Mon Sep 17 00:00:00 2001 From: o-dasher <88356162+o-dasher@users.noreply.github.com> Date: Tue, 27 Sep 2022 09:25:58 -0400 Subject: [PATCH 088/295] Removes unnecessary setting sources specifications --- osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs | 3 --- osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs | 7 ------- osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs | 3 --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 2 -- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 2 -- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 2 -- osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs | 7 ------- osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs | 2 ++ osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs | 3 --- osu.Game/Rulesets/Mods/ModNoScope.cs | 7 +++++++ osu.Game/Rulesets/Mods/ModWindDown.cs | 4 ---- osu.Game/Rulesets/Mods/ModWindUp.cs | 4 ---- 12 files changed, 9 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index 41d4590aa8..1adc969f8f 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -3,7 +3,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; @@ -16,7 +15,6 @@ namespace osu.Game.Rulesets.Catch.Mods { public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; - [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, @@ -24,7 +22,6 @@ namespace osu.Game.Rulesets.Catch.Mods Precision = 0.1f }; - [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] public override BindableBool ComboBasedSize { get; } = new BindableBool(true); public override float DefaultFlashlightSize => 350; diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs b/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs index 1b781b032c..19b4a39f97 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModNoScope.cs @@ -6,8 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Game.Rulesets.Mods; using osu.Framework.Utils; -using osu.Game.Configuration; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.UI; @@ -17,11 +15,6 @@ namespace osu.Game.Rulesets.Catch.Mods { public override LocalisableString Description => "Where's the catcher?"; - [SettingSource( - "Hidden at combo", - "The combo count at which the catcher becomes completely hidden", - SettingControlType = typeof(SettingsSlider) - )] public override BindableInt HiddenComboCount { get; } = new BindableInt(10) { MinValue = 0, diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs index 72ea1a2431..6eaede2112 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Layout; -using osu.Game.Configuration; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; using osuTK; @@ -17,7 +16,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModHidden) }; - [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, @@ -25,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.Mods Precision = 0.1f }; - [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] public override BindableBool ComboBasedSize { get; } = new BindableBool(); public override float DefaultFlashlightSize => 50; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index b2980f5311..f6622c268d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Configuration; namespace osu.Game.Rulesets.Osu.Mods { @@ -18,7 +17,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Hit them at the right size!"; - [SettingSource("Starting Size", "The initial size multiplier applied to all objects.")] public override BindableNumber StartScale { get; } = new BindableFloat(2) { MinValue = 1f, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 7b34fe05a9..79f5eed139 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -32,7 +32,6 @@ namespace osu.Game.Rulesets.Osu.Mods Precision = default_follow_delay, }; - [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, @@ -40,7 +39,6 @@ namespace osu.Game.Rulesets.Osu.Mods Precision = 0.1f }; - [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] public override BindableBool ComboBasedSize { get; } = new BindableBool(true); public override float DefaultFlashlightSize => 180; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index b49257e289..3d066d3ada 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Configuration; namespace osu.Game.Rulesets.Osu.Mods { @@ -18,7 +17,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Hit them at the right size!"; - [SettingSource("Starting Size", "The initial size multiplier applied to all objects.")] public override BindableNumber StartScale { get; } = new BindableFloat(0.5f) { MinValue = 0f, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index 84538cec30..2f84c30581 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -7,8 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.UI; @@ -22,11 +20,6 @@ namespace osu.Game.Rulesets.Osu.Mods private PeriodTracker spinnerPeriods = null!; - [SettingSource( - "Hidden at combo", - "The combo count at which the cursor becomes completely hidden", - SettingControlType = typeof(SettingsSlider) - )] public override BindableInt HiddenComboCount { get; } = new BindableInt(10) { MinValue = 0, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs index 59984f9a7b..6f1206382a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; @@ -20,6 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; + [SettingSource("Starting Size", "The initial size multiplier applied to all objects.")] public abstract BindableNumber StartScale { get; } protected virtual float EndScale => 1; diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 05baf88a33..1caacdd1d7 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Layout; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; @@ -17,7 +16,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1; - [SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")] public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1) { MinValue = 0.5f, @@ -25,7 +23,6 @@ namespace osu.Game.Rulesets.Taiko.Mods Precision = 0.1f }; - [SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")] public override BindableBool ComboBasedSize { get; } = new BindableBool(true); public override float DefaultFlashlightSize => 250; diff --git a/osu.Game/Rulesets/Mods/ModNoScope.cs b/osu.Game/Rulesets/Mods/ModNoScope.cs index 1b9ce833ad..36fbb88943 100644 --- a/osu.Game/Rulesets/Mods/ModNoScope.cs +++ b/osu.Game/Rulesets/Mods/ModNoScope.cs @@ -6,7 +6,9 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; @@ -34,6 +36,11 @@ namespace osu.Game.Rulesets.Mods protected float ComboBasedAlpha; + [SettingSource( + "Hidden at combo", + "The combo count at which the cursor becomes completely hidden", + SettingControlType = typeof(SettingsSlider) + )] public abstract BindableInt HiddenComboCount { get; } public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index 15ee37d481..35a673093b 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Configuration; namespace osu.Game.Rulesets.Mods { @@ -17,7 +16,6 @@ namespace osu.Game.Rulesets.Mods public override LocalisableString Description => "Sloooow doooown..."; public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown; - [SettingSource("Initial rate", "The starting speed of the track")] public override BindableNumber InitialRate { get; } = new BindableDouble(1) { MinValue = 0.51, @@ -25,7 +23,6 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; - [SettingSource("Final rate", "The speed increase to ramp towards")] public override BindableNumber FinalRate { get; } = new BindableDouble(0.75) { MinValue = 0.5, @@ -33,7 +30,6 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; - [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public override BindableBool AdjustPitch { get; } = new BindableBool(true); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray(); diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index 6d8838c5a5..bbc8382055 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Configuration; namespace osu.Game.Rulesets.Mods { @@ -17,7 +16,6 @@ namespace osu.Game.Rulesets.Mods public override LocalisableString Description => "Can you keep up?"; public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp; - [SettingSource("Initial rate", "The starting speed of the track")] public override BindableNumber InitialRate { get; } = new BindableDouble(1) { MinValue = 0.5, @@ -25,7 +23,6 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; - [SettingSource("Final rate", "The speed increase to ramp towards")] public override BindableNumber FinalRate { get; } = new BindableDouble(1.5) { MinValue = 0.51, @@ -33,7 +30,6 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; - [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public override BindableBool AdjustPitch { get; } = new BindableBool(true); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray(); From 0cffbb756286971645105c3794afd03bca3e4122 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Tue, 27 Sep 2022 16:54:24 +0200 Subject: [PATCH 089/295] Clamp `LifetimeStart` of `HitObject`'s to their judgement windows --- .../Objects/Drawables/DrawableHoldNoteTail.cs | 2 +- .../Objects/Drawables/DrawableSpinnerTick.cs | 2 +- .../Objects/Drawables/DrawableDrumRollTick.cs | 2 +- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 6 +++++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs index a7bdcd047e..3084f71be2 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public void UpdateResult() => base.UpdateResult(true); - protected override double MaximumJudgementOffset => base.MaximumJudgementOffset * release_window_lenience; + public override double MaximumJudgementOffset => base.MaximumJudgementOffset * release_window_lenience; protected override void CheckForResult(bool userTriggered, double timeOffset) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs index 6a15463a32..4975ca1248 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Origin = Anchor.Centre; } - protected override double MaximumJudgementOffset => DrawableSpinner.HitObject.Duration; + public override double MaximumJudgementOffset => DrawableSpinner.HitObject.Duration; /// /// Apply a judgement result. diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 705a0a8047..451c5a793b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Filled = HitObject.FirstTick }); - protected override double MaximumJudgementOffset => HitObject.HitWindow; + public override double MaximumJudgementOffset => HitObject.HitWindow; protected override void CheckForResult(bool userTriggered, double timeOffset) { diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index e218da918c..dec68a6c22 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -651,7 +651,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// This does not affect the time offset provided to invocations of . /// - protected virtual double MaximumJudgementOffset => HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? 0; + public virtual double MaximumJudgementOffset => HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? 0; /// /// Applies the of this , notifying responders such as diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 0ed3ca1e63..d0aca4e7fc 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Diagnostics; using osu.Framework.Allocation; @@ -214,7 +215,10 @@ namespace osu.Game.Rulesets.UI.Scrolling break; } - return scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength); + double computedStartTime = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength); + + // always load the hitobject before its first judgement offset + return Math.Min(hitObject.HitObject.StartTime - hitObject.MaximumJudgementOffset, computedStartTime); } private void updateLayoutRecursive(DrawableHitObject hitObject) From eae8916e76ac0b0795565fd1d93cdb10a5136e2a Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Tue, 27 Sep 2022 19:52:55 +0200 Subject: [PATCH 090/295] Add test --- .../Judgements/TestSceneHitJudgements.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs index a405f0e8ba..d2d5cdb6ac 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using NUnit.Framework; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; @@ -129,5 +130,32 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements AssertResult(0, HitResult.Miss); AssertResult(0, HitResult.IgnoreMiss); } + + [Test] + public void TestHighVelocityHit() + { + const double hit_time = 1000; + + var beatmap = CreateBeatmap(new Hit + { + Type = HitType.Centre, + StartTime = hit_time, + }); + + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 6 }); + beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 10 }); + + var hitWindows = new HitWindows(); + hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); + + PerformTest(new List + { + new TaikoReplayFrame(0), + new TaikoReplayFrame(hit_time - hitWindows.WindowFor(HitResult.Great), TaikoAction.LeftCentre), + }, beatmap); + + AssertJudgementCount(1); + AssertResult(0, HitResult.Ok); + } } } From 8edb1cb98a45127c2c484001269f87ae2435b6f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Sep 2022 15:57:30 +0900 Subject: [PATCH 091/295] Add test coverage of current score staying on screen --- .../Visual/Gameplay/TestSceneGameplayLeaderboard.cs | 10 ++++++++++ osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 13 +++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 72656c29b1..171ae829a9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -58,6 +58,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for some scores not masked away", () => leaderboard.ChildrenOfType().Any(s => leaderboard.ScreenSpaceDrawQuad.Contains(s.ScreenSpaceDrawQuad.Centre))); + + AddUntilStep("wait for tracked score fully visible", () => leaderboard.ScreenSpaceDrawQuad.Intersects(leaderboard.TrackedScore!.ScreenSpaceDrawQuad)); + + AddStep("change score to middle", () => playerScore.Value = 1000000); + AddWaitStep("wait for movement", 5); + AddUntilStep("wait for tracked score fully visible", () => leaderboard.ScreenSpaceDrawQuad.Intersects(leaderboard.TrackedScore!.ScreenSpaceDrawQuad)); + + AddStep("change score to first", () => playerScore.Value = 5000000); + AddWaitStep("wait for movement", 5); + AddUntilStep("wait for tracked score fully visible", () => leaderboard.ScreenSpaceDrawQuad.Intersects(leaderboard.TrackedScore!.ScreenSpaceDrawQuad)); } [Test] diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 2d816fbd55..e550c00390 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD private bool requiresScroll; private readonly OsuScrollContainer scroll; - private GameplayLeaderboardScore? trackedScore; + public GameplayLeaderboardScore? TrackedScore { get; private set; } private const int max_panels = 8; @@ -78,10 +78,10 @@ namespace osu.Game.Screens.Play.HUD if (isTracked) { - if (trackedScore != null) + if (TrackedScore != null) throw new InvalidOperationException("Cannot track more than one score."); - trackedScore = drawable; + TrackedScore = drawable; } drawable.Expanded.BindTo(Expanded); @@ -108,7 +108,7 @@ namespace osu.Game.Screens.Play.HUD public void Clear() { Flow.Clear(); - trackedScore = null; + TrackedScore = null; scroll.ScrollToStart(false); } @@ -119,9 +119,10 @@ namespace osu.Game.Screens.Play.HUD { base.Update(); - if (requiresScroll && trackedScore != null) + if (requiresScroll && TrackedScore != null) { - float scrollTarget = scroll.GetChildPosInContent(trackedScore) + trackedScore.DrawHeight / 2 - scroll.DrawHeight / 2; + float scrollTarget = scroll.GetChildPosInContent(TrackedScore) + TrackedScore.DrawHeight / 2 - scroll.DrawHeight / 2; + scroll.ScrollTo(scrollTarget); } From c8643ed2655afe7d4f7018bc767cc9bd385af5a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Sep 2022 15:34:35 +0900 Subject: [PATCH 092/295] Set `ClampExtension` to zero to stop jittering --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index e550c00390..fde895a1ca 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -42,6 +42,7 @@ namespace osu.Game.Screens.Play.HUD { scroll = new InputDisabledScrollContainer { + ClampExtension = 0, RelativeSizeAxes = Axes.Both, Child = Flow = new FillFlowContainer { @@ -92,14 +93,6 @@ namespace osu.Game.Screens.Play.HUD int displayCount = Math.Min(Flow.Count, max_panels); Height = displayCount * (GameplayLeaderboardScore.PANEL_HEIGHT + Flow.Spacing.Y); - // Add extra margin space to flow equal to height of leaderboard. - // This ensures the content is always on screen, but also accounts for the fact that scroll operations - // without animation were actually forcing the local score to a location it can't usually reside at. - // - // Basically, the local score was in the scroll extension region (due to always trying to scroll the - // local player to the middle of the display, but there being no other content below the local player - // to scroll up by). - Flow.Margin = new MarginPadding { Bottom = Height }; requiresScroll = displayCount != Flow.Count; return drawable; From cdcc8494c9cc9e967e89b5fa06eef21bfaa97092 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Sep 2022 16:10:19 +0900 Subject: [PATCH 093/295] Fix fade being applied for too long when leaderboard scrolls to start --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 2d816fbd55..226cff9da1 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Play.HUD float fadeBottom = scroll.Current + scroll.DrawHeight; float fadeTop = scroll.Current + panel_height; - if (scroll.Current <= 0) fadeTop -= panel_height; + if (scroll.IsScrolledToStart()) fadeTop -= panel_height; if (!scroll.IsScrolledToEnd()) fadeBottom -= panel_height; // logic is mostly shared with Leaderboard, copied here for simplicity. From 4ee435507d3f078a5507faacd002162db922cebe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Sep 2022 17:58:47 +0900 Subject: [PATCH 094/295] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index dd263d6aaa..c33937ad95 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c4e2a5168e..31b17fb8e1 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 622efcd63d..a9a83e2802 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -82,7 +82,7 @@ - + From 2493468800a3bd2371d70fd8ad1aa03ec8903ebb Mon Sep 17 00:00:00 2001 From: nanashi-1 Date: Sun, 2 Oct 2022 18:40:31 +0800 Subject: [PATCH 095/295] display highest combo instead of current combo --- osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs index 53981b9edc..ab3cf2950c 100644 --- a/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/SoloGameplayLeaderboard.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Play.HUD local.TotalScore.BindTarget = scoreProcessor.TotalScore; local.Accuracy.BindTarget = scoreProcessor.Accuracy; - local.Combo.BindTarget = scoreProcessor.Combo; + local.Combo.BindTarget = scoreProcessor.HighestCombo; // Local score should always show lower than any existing scores in cases of ties. local.DisplayOrder.Value = long.MaxValue; From b6701dd57892f4f585adb1494388768538fb69cc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 2 Oct 2022 15:29:53 +0300 Subject: [PATCH 096/295] Add failing test case --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index e2c825df0b..a26a7e97be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected new OutroPlayer Player => (OutroPlayer)base.Player; + private double currentBeatmapDuration; private double currentStoryboardDuration; private bool showResults = true; @@ -45,7 +46,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true)); AddStep("set dim level to 0", () => LocalConfig.SetValue(OsuSetting.DimLevel, 0)); AddStep("reset fail conditions", () => currentFailConditions = (_, _) => false); - AddStep("set storyboard duration to 2s", () => currentStoryboardDuration = 2000); + AddStep("set beatmap duration to 0s", () => currentBeatmapDuration = 0); + AddStep("set storyboard duration to 8s", () => currentStoryboardDuration = 8000); AddStep("set ShowResults = true", () => showResults = true); } @@ -151,6 +153,24 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("player exited", () => Stack.CurrentScreen == null); } + [Test] + public void TestPerformExitAfterOutro() + { + CreateTest(() => + { + AddStep("set beatmap duration to 4s", () => currentBeatmapDuration = 4000); + AddStep("set storyboard duration to 1s", () => currentStoryboardDuration = 1000); + }); + + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.CurrentTime >= currentStoryboardDuration); + AddStep("exit via pause", () => Player.ExitViaPause()); + AddAssert("player paused", () => !Player.IsResuming); + + AddStep("resume player", () => Player.Resume()); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); + } + protected override bool AllowFail => true; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); @@ -160,7 +180,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { var beatmap = new Beatmap(); - beatmap.HitObjects.Add(new HitCircle()); + beatmap.HitObjects.Add(new HitCircle { StartTime = currentBeatmapDuration }); return beatmap; } @@ -189,7 +209,7 @@ namespace osu.Game.Tests.Visual.Gameplay private event Func failConditions; public OutroPlayer(Func failConditions, bool showResults = true) - : base(false, showResults) + : base(showResults: showResults) { this.failConditions = failConditions; } From 59728b0ccb78fc7394cc15016cfe1c55fa45884b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 2 Oct 2022 15:30:06 +0300 Subject: [PATCH 097/295] Fix results display delegate potentially cancelled while not exiting --- osu.Game/Screens/Play/Player.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e31e18046e..9bc784411a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -566,9 +566,6 @@ namespace osu.Game.Screens.Play /// protected void PerformExit(bool showDialogFirst) { - // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). - resultsDisplayDelegate?.Cancel(); - // there is a chance that an exit request occurs after the transition to results has already started. // even in such a case, the user has shown intent, so forcefully return to this screen (to proceed with the upwards exit process). if (!this.IsCurrentScreen()) @@ -603,6 +600,9 @@ namespace osu.Game.Screens.Play } } + // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). + resultsDisplayDelegate?.Cancel(); + // The actual exit is performed if // - the pause / fail dialog was not requested // - the pause / fail dialog was requested but is already displayed (user showing intention to exit). From a810afafb39752affe1e8f59deef4afc36ed095e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 2 Oct 2022 15:37:56 +0300 Subject: [PATCH 098/295] Reschedule results display delegate to avoid potential softlocks in the future --- osu.Game/Screens/Play/Player.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9bc784411a..7721d5b912 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -780,19 +780,11 @@ namespace osu.Game.Screens.Play /// /// /// A final display will only occur once all work is completed in . This means that even after calling this method, the results screen will never be shown until ScoreProcessor.HasCompleted becomes . - /// - /// Calling this method multiple times will have no effect. /// /// Whether a minimum delay () should be added before the screen is displayed. private void progressToResults(bool withDelay) { - if (resultsDisplayDelegate != null) - // Note that if progressToResults is called one withDelay=true and then withDelay=false, this no-delay timing will not be - // accounted for. shouldn't be a huge concern (a user pressing the skip button after a results progression has already been queued - // may take x00 more milliseconds than expected in the very rare edge case). - // - // If required we can handle this more correctly by rescheduling here. - return; + resultsDisplayDelegate?.Cancel(); double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0; From 492f26c8328a4562b249911cd2810be2d3f99f28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 16:56:32 +0900 Subject: [PATCH 099/295] Update test step in line with new combo source --- .../Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs index 6b52e934b7..c852685b74 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSoloGameplayLeaderboard.cs @@ -66,7 +66,7 @@ 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.Combo.Value = v); + AddSliderStep("combo", 0, 1000, 0, v => scoreProcessor.HighestCombo.Value = v); } [Test] From c4dd23ed15c2818f9aab44361e0f1981e57ad901 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 17:22:30 +0900 Subject: [PATCH 100/295] Log token retrieval failures even when gameplay is allowed to continue --- osu.Game/Screens/Play/SubmittingPlayer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index be77304076..85d1ecea6b 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -76,6 +76,7 @@ namespace osu.Game.Screens.Play req.Success += r => { + Logger.Log($"Score submission token retrieved ({r.ID})"); token = r.ID; tcs.SetResult(true); }; @@ -104,6 +105,12 @@ namespace osu.Game.Screens.Play this.Exit(); }); } + else + { + // Gameplay is allowed to continue, but we still should keep track of the error. + // In the future, this should be visible to the user in some way. + Logger.Log($"Score submission token retrieval failed ({exception.Message})"); + } tcs.SetResult(false); } From 65369e96ebf1ee90e5a74dba974a79824b8e54f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 17:29:46 +0900 Subject: [PATCH 101/295] Ensure token retrieval failure logic only runs once --- osu.Game/Screens/Play/SubmittingPlayer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 85d1ecea6b..d56b9c23c8 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -92,6 +92,11 @@ namespace osu.Game.Screens.Play void handleTokenFailure(Exception exception) { + // This method may be invoked multiple times due to the Task.Wait call above. + // We only really care about the first error. + if (!tcs.TrySetResult(false)) + return; + if (HandleTokenRetrievalFailure(exception)) { if (string.IsNullOrEmpty(exception.Message)) @@ -111,8 +116,6 @@ namespace osu.Game.Screens.Play // In the future, this should be visible to the user in some way. Logger.Log($"Score submission token retrieval failed ({exception.Message})"); } - - tcs.SetResult(false); } } From 42aac16b377b7b8ab52fba8107a84a5d13fa87c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 18:12:36 +0900 Subject: [PATCH 102/295] Adjust leaderboard score panels sizing based on accuracy/combo width --- .../TestSceneSoloGameplayLeaderboard.cs | 3 +- .../Play/HUD/GameplayLeaderboardScore.cs | 44 +++++++++++++++---- 2 files changed, 37 insertions(+), 10 deletions(-) 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/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 29354e610d..548dd2a5bb 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,13 +325,32 @@ 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; + + // Schedule required to get correct DrawWidth from text after updates. + private void updateDetailsWidth() => SchedulerAfterChildren.AddOnce(() => + { + 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() { bool widthExtension = false; From d9f678d94220e8a3adefc9e058a521263d0fca9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 18:16:54 +0900 Subject: [PATCH 103/295] Change song select random key binding to not handle key repeat --- osu.Game/Screens/Select/FooterButtonRandom.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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; } From 0df217d85caf46e9dfaf1d9d9126b541a8a95034 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 3 Oct 2022 18:24:56 +0900 Subject: [PATCH 104/295] Add ability to adjust flashlight smoothness --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 6d7706cde2..7ed73b3163 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -201,6 +201,20 @@ namespace osu.Game.Rulesets.Mods } } + private float flashlightSmoothness = 1.1f; + + public float FlashlightSmoothness + { + get => flashlightSmoothness; + set + { + if (flashlightSmoothness == value) return; + + flashlightSmoothness = value; + Invalidate(Invalidation.DrawNode); + } + } + private class FlashlightDrawNode : DrawNode { protected new Flashlight Source => (Flashlight)base.Source; @@ -210,6 +224,7 @@ namespace osu.Game.Rulesets.Mods private Vector2 flashlightPosition; private Vector2 flashlightSize; private float flashlightDim; + private float flashlightSmoothness; private IVertexBatch? quadBatch; private Action? addAction; @@ -228,6 +243,7 @@ namespace osu.Game.Rulesets.Mods flashlightPosition = Vector2Extensions.Transform(Source.FlashlightPosition, DrawInfo.Matrix); flashlightSize = Source.FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy; flashlightDim = Source.FlashlightDim; + flashlightSmoothness = Source.flashlightSmoothness; } public override void Draw(IRenderer renderer) @@ -249,6 +265,7 @@ namespace osu.Game.Rulesets.Mods shader.GetUniform("flashlightPos").UpdateValue(ref flashlightPosition); shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize); shader.GetUniform("flashlightDim").UpdateValue(ref flashlightDim); + shader.GetUniform("flashlightSmoothness").UpdateValue(ref flashlightSmoothness); renderer.DrawQuad(renderer.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction); From 8598eb29f8393106b1dbea30c9b1b8fbdddd3418 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 3 Oct 2022 18:26:35 +0900 Subject: [PATCH 105/295] Adjust flashlight to closely match classic --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 79f5eed139..66f367c79b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override BindableBool ComboBasedSize { get; } = new BindableBool(true); - public override float DefaultFlashlightSize => 180; + public override float DefaultFlashlightSize => 200; private OsuFlashlight flashlight = null!; @@ -63,6 +63,7 @@ namespace osu.Game.Rulesets.Osu.Mods followDelay = modFlashlight.FollowDelay.Value; FlashlightSize = new Vector2(0, GetSizeFor(0)); + FlashlightSmoothness = 1.4f; } public void OnSliderTrackingChange(ValueChangedEvent e) From deae7cff60efdf4e58bbdcae9c7121565d575d2a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 3 Oct 2022 18:38:29 +0900 Subject: [PATCH 106/295] Adjust flashliht scaling to match classic --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 7ed73b3163..a594363d4c 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -151,9 +151,9 @@ namespace osu.Game.Rulesets.Mods if (comboBasedSize) { if (combo >= 200) - size *= 0.8f; + size *= 0.625f; else if (combo >= 100) - size *= 0.9f; + size *= 0.8125f; } return size; From 13ee5c179ea2dd338b3f9ef0001b4bb8a8941b90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 18:42:40 +0900 Subject: [PATCH 107/295] Add missing parenthesis in log message --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 59c0af1533..8a42fef43d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -563,7 +563,7 @@ namespace osu.Game 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); From 7fbbe88c8ea945dfd8aa4b51091c354074e2a2d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 20:24:09 +0900 Subject: [PATCH 108/295] Add test coverage of song select score presentation failures --- .../Navigation/TestScenePresentScore.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) 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) { From 332d63b53bee3d5ea18a4e7853aee834674d5039 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 20:04:37 +0900 Subject: [PATCH 109/295] Always return to main menu before attempting to present a score from import --- osu.Game/OsuGame.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8a42fef43d..6359ed3e27 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -561,6 +561,8 @@ 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)"); @@ -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) From dfb143ec0bb1483c61b8a7d72812b535ba241466 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Oct 2022 21:14:57 +0900 Subject: [PATCH 110/295] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index dd263d6aaa..102d368095 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c4e2a5168e..9cc4f3fbab 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 622efcd63d..cc922d304f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From e98b4b4fbd81273091472ef13dd30f8b8d0e0688 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 3 Oct 2022 23:14:21 +0200 Subject: [PATCH 111/295] Make `updateDisplayModeDropdowns` regular method --- .../Sections/Graphics/LayoutSettings.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 28642f12a1..f3483478ff 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -202,19 +202,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics // initial update bypasses transforms updateScalingModeVisibility(); - void updateDisplayModeDropdowns() - { - if (resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen) - resolutionDropdown.Show(); - else - resolutionDropdown.Hide(); - - if (displayDropdown.Items.Count() > 1) - displayDropdown.Show(); - else - displayDropdown.Hide(); - } - void updateScalingModeVisibility() { if (scalingMode.Value == ScalingMode.Off) @@ -225,6 +212,19 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } } + private void updateDisplayModeDropdowns() + { + if (resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen) + resolutionDropdown.Show(); + else + resolutionDropdown.Hide(); + + if (displayDropdown.Items.Count() > 1) + displayDropdown.Show(); + else + displayDropdown.Hide(); + } + private void updateScreenModeWarning() { if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS) From 31fefc1ee2d07bf1b137d9bc3822f84bb6a8495b Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 3 Oct 2022 23:23:02 +0200 Subject: [PATCH 112/295] Add nullable annotation to `LayoutSettings` --- .../Sections/Graphics/LayoutSettings.cs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index f3483478ff..dbedd01125 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Drawing; using System.Linq; @@ -29,31 +27,31 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { protected override LocalisableString Header => GraphicsSettingsStrings.LayoutHeader; - private FillFlowContainer> scalingSettings; + private FillFlowContainer> scalingSettings = null!; private readonly Bindable currentDisplay = new Bindable(); private readonly IBindableList windowModes = new BindableList(); - private Bindable scalingMode; - private Bindable sizeFullscreen; + private Bindable scalingMode = null!; + private Bindable sizeFullscreen = null!; private readonly BindableList resolutions = new BindableList(new[] { new Size(9999, 9999) }); private readonly IBindable fullscreenCapability = new Bindable(FullscreenCapability.Capable); [Resolved] - private OsuGameBase game { get; set; } + private OsuGameBase game { get; set; } = null!; [Resolved] - private GameHost host { get; set; } + private GameHost host { get; set; } = null!; - private SettingsDropdown resolutionDropdown; - private SettingsDropdown displayDropdown; - private SettingsDropdown windowModeDropdown; + private SettingsDropdown resolutionDropdown = null!; + private SettingsDropdown displayDropdown = null!; + private SettingsDropdown windowModeDropdown = null!; - private Bindable scalingPositionX; - private Bindable scalingPositionY; - private Bindable scalingSizeX; - private Bindable scalingSizeY; + private Bindable scalingPositionX = null!; + private Bindable scalingPositionY = null!; + private Bindable scalingSizeX = null!; + private Bindable scalingSizeY = null!; private const int transition_duration = 400; @@ -280,7 +278,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }; } - private Drawable preview; + private Drawable? preview; private void showPreview() { From efc9bed4f062010097e9a8a13deb2b3856c7822b Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 3 Oct 2022 23:52:04 +0200 Subject: [PATCH 113/295] Use `IWindow.DisplaysChanged` to update displays --- .../Sections/Graphics/LayoutSettings.cs | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index dbedd01125..42edd49a47 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Drawing; using System.Linq; using osu.Framework; @@ -44,6 +45,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics [Resolved] private GameHost host { get; set; } = null!; + private IWindow? window; + private SettingsDropdown resolutionDropdown = null!; private SettingsDropdown displayDropdown = null!; private SettingsDropdown windowModeDropdown = null!; @@ -58,6 +61,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics [BackgroundDependencyLoader] private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, GameHost host) { + window = host.Window; + scalingMode = osuConfig.GetBindable(OsuSetting.Scaling); sizeFullscreen = config.GetBindable(FrameworkSetting.SizeFullscreen); scalingSizeX = osuConfig.GetBindable(OsuSetting.ScalingSizeX); @@ -65,10 +70,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingPositionX = osuConfig.GetBindable(OsuSetting.ScalingPositionX); scalingPositionY = osuConfig.GetBindable(OsuSetting.ScalingPositionY); - if (host.Window != null) + if (window != null) { - currentDisplay.BindTo(host.Window.CurrentDisplayBindable); - windowModes.BindTo(host.Window.SupportedWindowModes); + currentDisplay.BindTo(window.CurrentDisplayBindable); + windowModes.BindTo(window.SupportedWindowModes); + window.DisplaysChanged += onDisplaysChanged; } if (host.Window is WindowsWindow windowsWindow) @@ -85,7 +91,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics displayDropdown = new DisplaySettingsDropdown { LabelText = GraphicsSettingsStrings.Display, - Items = host.Window?.Displays, + Items = window?.Displays, Current = currentDisplay, }, resolutionDropdown = new ResolutionSettingsDropdown @@ -210,6 +216,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } } + private void onDisplaysChanged(IEnumerable displays) + { + Scheduler.AddOnce(d => + { + displayDropdown.Items = d; + updateDisplayModeDropdowns(); + }, displays); + } + private void updateDisplayModeDropdowns() { if (resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen) @@ -289,6 +304,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics preview.Expire(); } + protected override void Dispose(bool isDisposing) + { + if (window != null) + window.DisplaysChanged -= onDisplaysChanged; + + base.Dispose(isDisposing); + } + private class ScalingPreview : ScalingContainer { public ScalingPreview() From 929eb8559e9101e1ce1ba6a2249d15c9531d17d0 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 3 Oct 2022 16:02:33 -0700 Subject: [PATCH 114/295] Fix `LegacySmoke` alpha calculations --- .../Skinning/Legacy/LegacySmoke.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index f02c20fefb..b8d70c1c6d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -67,6 +67,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected new LegacySmoke Source => (LegacySmoke)base.Source; private double initialFadeOutDurationTrunc; + private double firstVisiblePointTime; + private double initialFadeOutTime; private double reFadeInTime; private double finalFadeOutTime; @@ -83,20 +85,22 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { base.ApplyState(); - initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); rotationSeed = Source.RotationSeed; - rotationRNG = new Random(rotationSeed); - initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime); - reFadeInTime = re_fade_in_speed * (CurrentTime - SmokeEndTime) + SmokeEndTime - initialFadeOutDurationTrunc; - finalFadeOutTime = final_fade_out_speed * (CurrentTime - SmokeEndTime) + SmokeEndTime - initialFadeOutDurationTrunc * (1 + 1 / re_fade_in_speed); + + initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); + firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc; + + initialFadeOutTime = CurrentTime; + reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / re_fade_in_speed); + finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / final_fade_out_speed); } protected override Color4 PointColour(SmokePoint point) { var color = Color4.White; - double timeDoingInitialFadeOut = initialFadeOutTime - point.Time; + double timeDoingInitialFadeOut = Math.Min(initialFadeOutTime, SmokeEndTime) - point.Time; if (timeDoingInitialFadeOut > 0) { @@ -106,8 +110,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (color.A > 0) { - double timeDoingReFadeIn = reFadeInTime - point.Time; - double timeDoingFinalFadeOut = finalFadeOutTime - point.Time; + double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed; + double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed; if (timeDoingFinalFadeOut > 0) { From c1da3bc9cf831b35f368bb54c64e84bdede47339 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 3 Oct 2022 16:03:37 -0700 Subject: [PATCH 115/295] Remove skinnable parents at the same time as their smoke children --- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index 2b5bddf959..f8551e46e9 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; @@ -44,6 +45,9 @@ namespace osu.Game.Rulesets.Osu.UI { isSmoking = false; SmokeEnded?.Invoke(Time.Current); + + foreach (SkinnableDrawable skinnable in Children) + skinnable.LifetimeEnd = skinnable.Drawable.LifetimeEnd; } } From 343bdaa98efbc457ba5383fe4c488c0ba1297f7a Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 3 Oct 2022 16:07:39 -0700 Subject: [PATCH 116/295] Remove unnecessary `IsActive` variable --- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index 4d2a193a8a..fae875eedb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -40,7 +40,6 @@ namespace osu.Game.Rulesets.Osu.Skinning protected double SmokeEndTime { get; private set; } = double.MaxValue; protected virtual float PointInterval => Radius * 7f / 8; - protected bool IsActive { get; private set; } protected readonly List SmokePoints = new List(); @@ -73,7 +72,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { smokeContainer.SmokeMoved += onSmokeMoved; smokeContainer.SmokeEnded += onSmokeEnded; - IsActive = true; onSmokeMoved(smokeContainer.LastMousePosition, Time.Current); } @@ -87,9 +85,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private void onSmokeMoved(Vector2 position, double time) { - if (!IsActive) - return; - lastPosition ??= position; float delta = (position - (Vector2)lastPosition).LengthFast; @@ -137,10 +132,12 @@ namespace osu.Game.Rulesets.Osu.Skinning private void onSmokeEnded(double time) { - if (!IsActive) - return; + if (smokeContainer != null) + { + smokeContainer.SmokeMoved -= onSmokeMoved; + smokeContainer.SmokeEnded -= onSmokeEnded; + } - IsActive = false; SmokeEndTime = time; } From ccef189b81569aa25b0d2ce26c03039be9a7aeea Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 3 Oct 2022 18:19:05 -0700 Subject: [PATCH 117/295] Add barebones test for smoke --- .../metrics-skin/cursor-smoke@2x.png | Bin 0 -> 251 bytes .../Resources/old-skin/cursor-smoke.png | Bin 0 -> 2249 bytes osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs | 97 ++++++++++++++++++ osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 4 +- 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/cursor-smoke@2x.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor-smoke.png create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/cursor-smoke@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/cursor-smoke@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b1380a47a47f4e1b293237f24f54606600a72964 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hEk44ofy`glX=O&z`&C3 z=fQf_yHCHWP5&9ixMnE>k_1=7RuP75 qO@?bh3?xZhWPgz-fY5j948IsFPib1&&npZJ3=E#GelF{r5}E*P-cP*% literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor-smoke.png b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor-smoke.png new file mode 100644 index 0000000000000000000000000000000000000000..5f7beae4e9dddd9493c3353ae209ea8c5679ca9c GIT binary patch literal 2249 zcmaJ@dsLEX8vg_lFX44cEvG)I+0+vF6ck8Q6mJ<0BIM<`DZ;0q!iQYK$W%02GB0J> z)h(xVvNo-4S}&P4)ybS)#nH@iYt4$Li#1bPS!%Thvu6HS_c`bN<9*Ka`#rzs@}Bc< z=fp(dtQT7Y0Dy~%WN`ri08Piz0ssJ|R{IJ705c^wA_QnMxIF;?z&wM=W&*(9D{W>3 z7ytmQX!hkV2q%3rRi@=uaa+6fzBhR)any z$d?F_h(0uL2%?ky>5v~dXZS#zH3$YMMCo)cD|{}ULX^(Hi&ZK)ok-MZGz5(=0a0WS zAsUTFB>511e7qH+bZ_M*nM#oBEmJQ4L^4Oif|WvrM6Qw`GSEaTNJG>r1|I*p6si0R zw@f+LFCii&<_hFQh(I!x_Niv6oX!6Kp;GCWXr+n^|1SD}5-WL|y7D-pFq2y@}| z<)&0}dZ+>xs1OAYL9#!0lOsk{h*FHm!O&39BVHmCAsVIUY&e@ukCG`>0+|qwVlnW! zQaM2)5z+la!^0^M%`YS@%nyRXe5qkHYM4KhNe(6XlKg4@pRp`NsFuPq)n}~ecdY-s z*x4DGEMT@NbQY|TY=lMO3PcLd7EYJUFNr)a?HpD#za+~1SmKdK_CVp9G)9uTXfMp^EUV#q^Y&1rH007LQC>E2K5b@WWiIsf5J!Z6%eI4{FMjQG; z$A~@RvVPVkC?GZOjosU8Z_$oR%aMcj)wNyj(Bi9Bm#iGBzmqhL-R>CEPN6oYtV-RQq(=C)@gv&sTMpoEyn#emCMbb_lU- zZmFPJ{_@&+Tz0|Xsi6b;^?6VF6-AF9Ci)Ui*SW*2%1wKd8*z^3@y#h$yXvIV6wF!q zDJ#|`8m9Xq?UvJ>g)Z%mApC37X#MEkRN)zl4kYj98+*^{%9}BdP{PgV6$Q5TR0sW# z%Ta%9frZ|EJmN)*=1Elh_l+lYF0=x>NJ;U?g!}FHoYb(-gmW~dbP9hYQF>Xv}kH6B&GP;NAi$@#9ddHBU9u@kDpHd|cmYlc1idi&bJ zqb;;M%K}_Hk|*xvfnh^At)3OmsQj8elB@BnNFL$U8_FI|wH4M49J5?zX@7JrtzdQV zPS-dmJFLdMboXi8^|fSFhZ1^DLiqjGV;w)9sO>s!*2ehw70IE%l~e3BX%` za+2%&(pTV{n$B4v3R>bpXlpr8?(srfO9-?c)CYPcV2>B46mfg$Pnd%n^oPsuNB2r< z;!r}H?hLcsmf-%_A7|DS7n^MlvJG6Y6jy;!Lpd8S1o^AcPsV#_DP4x2tRAhnQCiAy zeP+YD=cuiFFkM@>(U`xW6IX*;f?r4RBuzFK{;>C{%RqNj$za(Qu`yiL)8+SW^;`VT z-{N#Tx-lhXLUHIB_qMyXRMztY$n*8;Q2FG$E%TxQSTLvZ_(?#=rT(5=)c%HS+&Iyg_qYM9WMVd$H{nZSM=JPz_0WP0E_F?@N%m0>?&R|<=~e^oAtz$rcPsAa%UP8cYUdX>@>+sA zER0xQ6N>KGD#|v#9|K$`x7MuJQZ;GD+ewnzr5TeJr8xJJpCDA^#IRz6HPE(yX8Npq z-%{#6_6DC* z`^(oo?L)WRTdBj{sC;ZZ;;ZyZs@U>!@%Wvx32tFrBl91lRSx~fbyux>-S+L#P-+3% zjR=Rnq38p&%-|eulJ-6KAT`4;n_`~;M0WqMx2$e=P>UPp>B{|M@guKKym3bFTbMDB zYS$M3a^_U&g;SoKq3WG?or^T+6c)F1{lUC^#@}|J%`yWMSsz_<w@TX}}z)rOno{x|xSL(_gSlOKZ)9h$~w+kK=RzWQyW=|2$_7Q<=^NiF&> DAWL(? literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs new file mode 100644 index 0000000000..ce1cab3b9b --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs @@ -0,0 +1,97 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Framework.Input.States; +using osu.Framework.Testing.Input; +using osu.Game.Rulesets.Osu.UI; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestSceneSmoke : OsuSkinnableTestScene + { + [Test] + public void TestSmoking() + { + AddStep("Create smoke", () => + { + SetContents(_ => + { + return new SmokingInputManager + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.95f), + Child = new TestSmokeContainer { RelativeSizeAxes = Axes.Both }, + }; + }); + }); + } + + private const double spin_duration = 5_000; + private const float spin_angle = 4 * MathF.PI; + + private class SmokingInputManager : ManualInputManager + { + private double? startTime; + + public SmokingInputManager() + { + UseParentInput = false; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + MoveMouseTo(ToScreenSpace(DrawSize / 2)); + } + + protected override void Update() + { + base.Update(); + + startTime ??= Time.Current; + + float fraction = (float)((Time.Current - startTime) / spin_duration); + + float angle = fraction * spin_angle; + float radius = fraction * Math.Min(DrawSize.X, DrawSize.Y) / 2; + + Vector2 pos = radius * new Vector2(MathF.Cos(angle), MathF.Sin(angle)) + DrawSize / 2; + MoveMouseTo(ToScreenSpace(pos)); + } + } + + private class TestSmokeContainer : SmokeContainer + { + private double? startTime; + private bool isPressing; + private bool isFinished; + + protected override void Update() + { + base.Update(); + + startTime ??= Time.Current; + + if (!isPressing && !isFinished && Time.Current > startTime + 0.1) + { + OnPressed(new KeyBindingPressEvent(new InputState(), OsuAction.Smoke)); + isPressing = true; + isFinished = false; + } + + if (isPressing && Time.Current > startTime + spin_duration) + { + OnReleased(new KeyBindingReleaseEvent(new InputState(), OsuAction.Smoke)); + isPressing = false; + isFinished = true; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index f8551e46e9..449dd920d6 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Game.Rulesets.Osu.Skinning; +using osu.Framework.Logging; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Osu.UI { if (e.Action == OsuAction.Smoke) { + Logger.Log("holy moly"); + isSmoking = true; AddInternal(new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Smoke), _ => new DefaultSmoke())); From eaab0deef32f037a2de634c8eb0406d95cc601c8 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Mon, 3 Oct 2022 19:20:51 -0700 Subject: [PATCH 118/295] Fix InspectCode issues --- osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs | 11 ++++------- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 6 +++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs index ce1cab3b9b..82417d09a7 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs @@ -19,14 +19,11 @@ namespace osu.Game.Rulesets.Osu.Tests { AddStep("Create smoke", () => { - SetContents(_ => + SetContents(_ => new SmokingInputManager { - return new SmokingInputManager - { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.95f), - Child = new TestSmokeContainer { RelativeSizeAxes = Axes.Both }, - }; + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.95f), + Child = new TestSmokeContainer { RelativeSizeAxes = Axes.Both }, }); }); } diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index 449dd920d6..e139e16ff2 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -48,8 +49,11 @@ namespace osu.Game.Rulesets.Osu.UI isSmoking = false; SmokeEnded?.Invoke(Time.Current); - foreach (SkinnableDrawable skinnable in Children) + foreach (Drawable child in Children) + { + var skinnable = (SkinnableDrawable)child; skinnable.LifetimeEnd = skinnable.Drawable.LifetimeEnd; + } } } From 97207c11f55f5a0f464a67c7543c312e2b81ee7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 18:52:21 +0900 Subject: [PATCH 119/295] Add base transformer for osu!mania argon skin --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 +++ .../Argon/ManiaArgonSkinTransformer.cs | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 061dedb07a..c6b20b1baf 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -26,6 +26,7 @@ using osu.Game.Rulesets.Mania.Edit.Setup; using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Mania.Skinning.Argon; using osu.Game.Rulesets.Mania.Skinning.Legacy; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -68,6 +69,9 @@ namespace osu.Game.Rulesets.Mania { case LegacySkin: return new ManiaLegacySkinTransformer(skin, beatmap); + + case ArgonSkin: + return new ManiaArgonSkinTransformer(skin); } return null; diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs new file mode 100644 index 0000000000..67dd484923 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public class ManiaArgonSkinTransformer : SkinTransformer + { + public ManiaArgonSkinTransformer(ISkin skin) + : base(skin) + { + } + + public override Drawable? GetDrawableComponent(ISkinComponent component) + { + switch (component) + { + case ManiaSkinComponent maniaComponent: + switch (maniaComponent.Component) + { + case ManiaSkinComponents.KeyArea: + return new ArgonKeyArea(); + + // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. + } + + break; + } + + return base.GetDrawableComponent(component); + } + } +} From 326a3e65834a3950ccd49c361e17cac6a88f5ed5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 18:52:35 +0900 Subject: [PATCH 120/295] Add TODO in osu! argon transformer regarding missing components --- osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs index 7bc6723afb..3794350f6a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs @@ -56,6 +56,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon case OsuSkinComponents.CursorTrail: return new ArgonCursorTrail(); + + // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. } break; From 83e7cc1e09b45adcd1454f63d74b0dd9c5e5c6d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 18:52:43 +0900 Subject: [PATCH 121/295] Add argon key area --- .../Skinning/Argon/ArgonKeyArea.cs | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs new file mode 100644 index 0000000000..055c21390c --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -0,0 +1,167 @@ +// 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.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public class ArgonKeyArea : CompositeDrawable, IKeyBindingHandler + { + private const float key_icon_size = 10; + + private readonly IBindable direction = new Bindable(); + + private Container directionContainer = null!; + private Container keyIcon = null!; + private Drawable gradient = null!; + + [Resolved] + private Column column { get; set; } = null!; + + public ArgonKeyArea() + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(IScrollingInfo scrollingInfo) + { + const float icon_circle_size = 8; + const float icon_spacing = 8; + const float icon_vertical_offset = 20; + + InternalChild = directionContainer = new Container + { + RelativeSizeAxes = Axes.X, + Height = Stage.HIT_TARGET_POSITION, + Children = new[] + { + gradient = new Box + { + Name = "Key gradient", + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f + }, + keyIcon = new Container + { + Name = "Icons", + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Children = new[] + { + new Circle + { + Y = icon_vertical_offset, + Size = new Vector2(icon_circle_size), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Blending = BlendingParameters.Additive, + Colour = column.AccentColour, + Masking = true, + }, + new Circle + { + X = -icon_spacing, + Y = icon_vertical_offset + icon_spacing * 1.2f, + Size = new Vector2(icon_circle_size), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Blending = BlendingParameters.Additive, + Colour = column.AccentColour, + Masking = true, + }, + new Circle + { + X = icon_spacing, + Y = icon_vertical_offset + icon_spacing * 1.2f, + Size = new Vector2(icon_circle_size), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Blending = BlendingParameters.Additive, + Colour = column.AccentColour, + Masking = true, + } + } + }, + } + }; + + direction.BindTo(scrollingInfo.Direction); + direction.BindValueChanged(onDirectionChanged, true); + } + + private void onDirectionChanged(ValueChangedEvent direction) + { + switch (direction.NewValue) + { + case ScrollingDirection.Up: + directionContainer.Scale = new Vector2(1, -1); + directionContainer.Anchor = Anchor.TopLeft; + directionContainer.Origin = Anchor.BottomLeft; + gradient.Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)); + break; + + case ScrollingDirection.Down: + directionContainer.Scale = new Vector2(1, 1); + directionContainer.Anchor = Anchor.BottomLeft; + directionContainer.Origin = Anchor.BottomLeft; + gradient.Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Color4.Black); + break; + } + } + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == column.Action.Value) + { + foreach (var circle in keyIcon.Children) + { + circle.ScaleTo(1.1f, 50, Easing.OutQuint); + + circle.FadeColour(Color4.White, 50, Easing.OutQuint); + circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Color4.White.Opacity(0.05f), + Radius = 10, + }, 50, Easing.OutQuint); + } + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + if (e.Action == column.Action.Value) + { + foreach (var circle in keyIcon.Children) + { + circle.ScaleTo(1f, 125, Easing.OutQuint); + + circle.FadeColour(column.AccentColour, 200, Easing.OutQuint); + circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Color4.White.Opacity(0), + Radius = 10, + }, 200, Easing.OutQuint); + } + } + } + } +} From 4718f4ac249017a695c5421fbc9e3c3ac1897228 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 19:13:37 +0900 Subject: [PATCH 122/295] Add second icon and improve glow effect --- .../Skinning/Argon/ArgonKeyArea.cs | 66 ++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 055c21390c..1bda832b76 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -1,11 +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 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.Effects; using osu.Framework.Graphics.Shapes; @@ -20,13 +20,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { public class ArgonKeyArea : CompositeDrawable, IKeyBindingHandler { - private const float key_icon_size = 10; - private readonly IBindable direction = new Bindable(); private Container directionContainer = null!; - private Container keyIcon = null!; - private Drawable gradient = null!; + private Container keyIcon = null!; + private Drawable background = null!; [Resolved] private Column column { get; set; } = null!; @@ -40,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private void load(IScrollingInfo scrollingInfo) { const float icon_circle_size = 8; - const float icon_spacing = 8; - const float icon_vertical_offset = 20; + const float icon_spacing = 7; + const float icon_vertical_offset = -30; InternalChild = directionContainer = new Container { @@ -49,13 +47,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Height = Stage.HIT_TARGET_POSITION, Children = new[] { - gradient = new Box + background = new Box { Name = "Key gradient", RelativeSizeAxes = Axes.Both, - Alpha = 0.5f + Colour = column.AccentColour.Darken(0.6f), }, - keyIcon = new Container + keyIcon = new Container { Name = "Icons", RelativeSizeAxes = Axes.Both, @@ -67,8 +65,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { Y = icon_vertical_offset, Size = new Vector2(icon_circle_size), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, Blending = BlendingParameters.Additive, Colour = column.AccentColour, Masking = true, @@ -78,8 +76,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon X = -icon_spacing, Y = icon_vertical_offset + icon_spacing * 1.2f, Size = new Vector2(icon_circle_size), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, Blending = BlendingParameters.Additive, Colour = column.AccentColour, Masking = true, @@ -89,11 +87,30 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon X = icon_spacing, Y = icon_vertical_offset + icon_spacing * 1.2f, Size = new Vector2(icon_circle_size), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, Blending = BlendingParameters.Additive, Colour = column.AccentColour, Masking = true, + }, + new CircularContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Y = -icon_vertical_offset, + Size = new Vector2(22, 14), + Masking = true, + BorderThickness = 4, + BorderColour = Color4.White, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + }, } } }, @@ -112,14 +129,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon directionContainer.Scale = new Vector2(1, -1); directionContainer.Anchor = Anchor.TopLeft; directionContainer.Origin = Anchor.BottomLeft; - gradient.Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)); break; case ScrollingDirection.Down: directionContainer.Scale = new Vector2(1, 1); directionContainer.Anchor = Anchor.BottomLeft; directionContainer.Origin = Anchor.BottomLeft; - gradient.Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Color4.Black); break; } } @@ -128,7 +143,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (e.Action == column.Action.Value) { - foreach (var circle in keyIcon.Children) + foreach (var circle in keyIcon.Children.OfType()) { circle.ScaleTo(1.1f, 50, Easing.OutQuint); @@ -136,8 +151,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = Color4.White.Opacity(0.05f), - Radius = 10, + Colour = Color4.White.Opacity(circle is Circle ? 0.05f : 0.2f), + Radius = 40, }, 50, Easing.OutQuint); } } @@ -149,16 +164,19 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (e.Action == column.Action.Value) { - foreach (var circle in keyIcon.Children) + foreach (var circle in keyIcon.Children.OfType()) { circle.ScaleTo(1f, 125, Easing.OutQuint); - circle.FadeColour(column.AccentColour, 200, Easing.OutQuint); + // TODO: temp lol + if (circle is Circle) + circle.FadeColour(column.AccentColour, 200, Easing.OutQuint); + circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = Color4.White.Opacity(0), - Radius = 10, + Radius = 30, }, 200, Easing.OutQuint); } } From d32eb6456112ef071fc5ccfc98ea74ba2f4e8007 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 19:32:58 +0900 Subject: [PATCH 123/295] Adjust colour application to stay around a bit longer --- .../Skinning/Argon/ArgonKeyArea.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 1bda832b76..63e00041b8 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -143,9 +143,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (e.Action == column.Action.Value) { + background + .FadeColour(column.AccentColour.Lighten(0.3f), 50, Easing.OutQuint).Then() + .FadeColour(column.AccentColour, 100, Easing.OutQuint); + foreach (var circle in keyIcon.Children.OfType()) { - circle.ScaleTo(1.1f, 50, Easing.OutQuint); + circle.ScaleTo(0.9f, 50, Easing.OutQuint); circle.FadeColour(Color4.White, 50, Easing.OutQuint); circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters @@ -164,20 +168,22 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (e.Action == column.Action.Value) { + background.FadeColour(column.AccentColour.Darken(0.6f), 800, Easing.OutQuint); + foreach (var circle in keyIcon.Children.OfType()) { - circle.ScaleTo(1f, 125, Easing.OutQuint); + circle.ScaleTo(1f, 200, Easing.OutQuint); // TODO: temp lol if (circle is Circle) - circle.FadeColour(column.AccentColour, 200, Easing.OutQuint); + circle.FadeColour(column.AccentColour, 800, Easing.OutQuint); circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = Color4.White.Opacity(0), Radius = 30, - }, 200, Easing.OutQuint); + }, 800, Easing.OutQuint); } } } From 36e2f5c512783d9f542f23fb4e7a4b69efceaa59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Sep 2022 19:48:27 +0900 Subject: [PATCH 124/295] Add argon hit target pieces --- .../Skinning/Argon/ArgonHitTarget.cs | 33 +++++++++++++++++++ .../Skinning/Argon/ArgonKeyArea.cs | 27 +++++++++++++-- .../Argon/ManiaArgonSkinTransformer.cs | 3 ++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs new file mode 100644 index 0000000000..6518607c9d --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Mania.Skinning.Default; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public class ArgonHitTarget : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + Height = DefaultNotePiece.NOTE_HEIGHT; + + InternalChildren = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.3f, + Blending = BlendingParameters.Additive, + Colour = Color4.White + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 63e00041b8..f2acc61416 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -26,6 +27,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private Container keyIcon = null!; private Drawable background = null!; + private Circle hitTargetLine = null!; + [Resolved] private Column column { get; set; } = null!; @@ -61,6 +64,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Origin = Anchor.TopCentre, Children = new[] { + hitTargetLine = new Circle() + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = OsuColour.Gray(196 / 255f), + Height = 4, + Masking = true, + }, new Circle { Y = icon_vertical_offset, @@ -149,13 +161,18 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon foreach (var circle in keyIcon.Children.OfType()) { - circle.ScaleTo(0.9f, 50, Easing.OutQuint); + if (circle != hitTargetLine) + circle.ScaleTo(0.9f, 50, Easing.OutQuint); circle.FadeColour(Color4.White, 50, Easing.OutQuint); + + // TODO: VERY TMPOERAOIRY. + float f = circle == hitTargetLine ? 0.2f : (circle is Circle ? 0.05f : 0.2f); + circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = Color4.White.Opacity(circle is Circle ? 0.05f : 0.2f), + Colour = Color4.White.Opacity(f), Radius = 40, }, 50, Easing.OutQuint); } @@ -175,7 +192,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon circle.ScaleTo(1f, 200, Easing.OutQuint); // TODO: temp lol - if (circle is Circle) + if (circle == hitTargetLine) + { + circle.FadeColour(OsuColour.Gray(196 / 255f), 800, Easing.OutQuint); + } + else if (circle is Circle) circle.FadeColour(column.AccentColour, 800, Easing.OutQuint); circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 67dd484923..dab71a65c3 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case ManiaSkinComponent maniaComponent: switch (maniaComponent.Component) { + case ManiaSkinComponents.HitTarget: + return new ArgonHitTarget(); + case ManiaSkinComponents.KeyArea: return new ArgonKeyArea(); From 21620bee1adce55fb12a6b9b65f7f0ad4ed0e675 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 4 Oct 2022 14:58:00 +0900 Subject: [PATCH 125/295] Attempt to fix deadlock in test --- ...stSceneOnlinePlayBeatmapAvailabilityTracker.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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)); } } From 5d80950eaf51f01f37471bf59f46f27f67d4f809 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 Oct 2022 14:01:36 +0900 Subject: [PATCH 126/295] Compute lifetime from entry in scrolling container --- .../Scrolling/ScrollingHitObjectContainer.cs | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index d0aca4e7fc..b012abb6b5 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Framework.Layout; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -127,6 +127,16 @@ namespace osu.Game.Rulesets.UI.Scrolling private float scrollLength => scrollingAxis == Direction.Horizontal ? DrawWidth : DrawHeight; + public override void Add(HitObjectLifetimeEntry entry) + { + // Scroll info is not available until loaded. + // The lifetime of all entries will be updated in the first Update. + if (IsLoaded) + setComputedLifetimeStart(entry); + + base.Add(entry); + } + protected override void AddDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable) { base.AddDrawable(entry, drawable); @@ -145,7 +155,6 @@ namespace osu.Game.Rulesets.UI.Scrolling private void invalidateHitObject(DrawableHitObject hitObject) { - hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject); layoutComputed.Remove(hitObject); } @@ -157,10 +166,8 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Clear(); - // Reset lifetime to the conservative estimation. - // If a drawable becomes alive by this lifetime, its lifetime will be updated to a more precise lifetime in the next update. foreach (var entry in Entries) - entry.SetInitialLifetime(); + setComputedLifetimeStart(entry); scrollingInfo.Algorithm.Reset(); @@ -187,38 +194,46 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject) + /// + /// Get a conservative maximum bounding box of a corresponding to . + /// It is used to calculate when the hit object appears. + /// + protected virtual RectangleF GetConservativeBoundingBox(HitObjectLifetimeEntry entry) => new RectangleF().Inflate(100); + + private double computeDisplayStartTime(HitObjectLifetimeEntry entry) { - // Origin position may be relative to the parent size - Debug.Assert(hitObject.Parent != null); + RectangleF boundingBox = GetConservativeBoundingBox(entry); + float startOffset = 0; - float originAdjustment = 0.0f; - - // calculate the dimension of the part of the hitobject that should already be visible - // when the hitobject origin first appears inside the scrolling container switch (direction.Value) { - case ScrollingDirection.Up: - originAdjustment = hitObject.OriginPosition.Y; + case ScrollingDirection.Right: + startOffset = boundingBox.Right; break; case ScrollingDirection.Down: - originAdjustment = hitObject.DrawHeight - hitObject.OriginPosition.Y; + startOffset = boundingBox.Bottom; break; case ScrollingDirection.Left: - originAdjustment = hitObject.OriginPosition.X; + startOffset = -boundingBox.Left; break; - case ScrollingDirection.Right: - originAdjustment = hitObject.DrawWidth - hitObject.OriginPosition.X; + case ScrollingDirection.Up: + startOffset = -boundingBox.Top; break; } - double computedStartTime = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength); + return scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength); + } + + private void setComputedLifetimeStart(HitObjectLifetimeEntry entry) + { + double computedStartTime = computeDisplayStartTime(entry); // always load the hitobject before its first judgement offset - return Math.Min(hitObject.HitObject.StartTime - hitObject.MaximumJudgementOffset, computedStartTime); + double judgementOffset = entry.HitObject.HitWindows?.WindowFor(Scoring.HitResult.Miss) ?? 0; + entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - judgementOffset, computedStartTime); } private void updateLayoutRecursive(DrawableHitObject hitObject) From 781f5420b0d1aa0a1f359c0f74e59a2787851ce8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 Oct 2022 15:17:11 +0900 Subject: [PATCH 127/295] Add test for scrolling hit object lifetime --- .../Gameplay/TestSceneScrollingHitObjects.cs | 70 +++++++++++++++---- .../UI/Scrolling/ScrollingPlayfield.cs | 4 +- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs index 8a4818d2f8..98afbe92b1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs @@ -11,8 +11,10 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Threading; +using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -167,14 +169,39 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("add control points", () => addControlPoints(testControlPoints, Time.Current)); } - private void addHitObject(double time) + [Test] + public void TestVeryFlowScroll() + { + const double long_time_range = 100000; + var manualClock = new ManualClock(); + + AddStep("set manual clock", () => + { + manualClock.CurrentTime = 0; + scrollContainers.ForEach(c => c.Clock = new FramedClock(manualClock)); + + setScrollAlgorithm(ScrollVisualisationMethod.Constant); + scrollContainers.ForEach(c => c.TimeRange = long_time_range); + }); + + AddStep("add hit objects", () => + { + addHitObject(long_time_range); + addHitObject(long_time_range + 100, 250); + }); + + AddAssert("hit objects are alive", () => playfields.All(p => p.HitObjectContainer.AliveObjects.Count() == 2)); + } + + private void addHitObject(double time, float size = 75) { playfields.ForEach(p => { - var hitObject = new TestDrawableHitObject(time); - setAnchor(hitObject, p); + var hitObject = new TestHitObject(size) { StartTime = time }; + var drawable = new TestDrawableHitObject(hitObject); - p.Add(hitObject); + setAnchor(drawable, p); + p.Add(drawable); }); } @@ -248,6 +275,8 @@ namespace osu.Game.Tests.Visual.Gameplay } }; } + + protected override ScrollingHitObjectContainer CreateScrollingHitObjectContainer() => new TestScrollingHitObjectContainer(); } private class TestDrawableControlPoint : DrawableHitObject @@ -281,22 +310,39 @@ namespace osu.Game.Tests.Visual.Gameplay } } - private class TestDrawableHitObject : DrawableHitObject + private class TestHitObject : HitObject { - public TestDrawableHitObject(double time) - : base(new HitObject { StartTime = time, HitWindows = HitWindows.Empty }) - { - Origin = Anchor.Custom; - OriginPosition = new Vector2(75 / 4.0f); + public readonly float Size; - AutoSizeAxes = Axes.Both; + public TestHitObject(float size) + { + Size = size; + } + } + + private class TestDrawableHitObject : DrawableHitObject + { + public TestDrawableHitObject(TestHitObject hitObject) + : base(hitObject) + { + Origin = Anchor.Centre; + Size = new Vector2(hitObject.Size); AddInternal(new Box { - Size = new Vector2(75), + RelativeSizeAxes = Axes.Both, Colour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1) }); } } + + private class TestScrollingHitObjectContainer : ScrollingHitObjectContainer + { + protected override RectangleF GetConservativeBoundingBox(HitObjectLifetimeEntry entry) + { + var hitObject = (TestHitObject)entry.HitObject; + return new RectangleF().Inflate(hitObject.Size / 2); + } + } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 078f06b745..34e5b7f9de 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.UI.Scrolling /// public virtual Vector2 ScreenSpacePositionAtTime(double time) => HitObjectContainer.ScreenSpacePositionAtTime(time); - protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(); + protected sealed override HitObjectContainer CreateHitObjectContainer() => CreateScrollingHitObjectContainer(); + + protected virtual ScrollingHitObjectContainer CreateScrollingHitObjectContainer() => new ScrollingHitObjectContainer(); } } From 1cccd03480dcf9c54031ac564a0e947ecb76d56e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 Oct 2022 15:03:04 +0900 Subject: [PATCH 128/295] Fix scrolling nested hit object lifetime not set --- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index b012abb6b5..37da157cc1 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -251,8 +251,9 @@ namespace osu.Game.Rulesets.UI.Scrolling { updateLayoutRecursive(obj); - // Nested hitobjects don't need to scroll, but they do need accurate positions + // Nested hitobjects don't need to scroll, but they do need accurate positions and start lifetime updatePosition(obj, hitObject.HitObject.StartTime); + setComputedLifetimeStart(obj.Entry); } } From 2aa4d21c757c84dd4ddefffd3620b326f7b6b0d8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 Oct 2022 15:08:53 +0900 Subject: [PATCH 129/295] Remove code that is not needed anymore --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs | 4 ---- .../Objects/Drawables/DrawableManiaHitObject.cs | 4 ---- 2 files changed, 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs index 6020348938..a607ed572d 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs @@ -54,10 +54,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } } - protected override void UpdateInitialTransforms() - { - } - protected override void UpdateStartTimeStateTransforms() => this.FadeOut(150); } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index bcc10ab7bc..6cd39d835d 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -23,10 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected readonly IBindable Direction = new Bindable(); - // Leaving the default (10s) makes hitobjects not appear, as this offset is used for the initial state transforms. - // Calculated as DrawableManiaRuleset.MAX_TIME_RANGE + some additional allowance for velocity < 1. - protected override double InitialLifetimeOffset => 30000; - [Resolved(canBeNull: true)] private ManiaPlayfield playfield { get; set; } From 1ffa0afafc2663b780f93a48cca3d048d601daf0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 16:05:36 +0900 Subject: [PATCH 130/295] Tweak visuals and fix up code quality --- .../Skinning/Argon/ArgonKeyArea.cs | 189 ++++++++++-------- 1 file changed, 107 insertions(+), 82 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index f2acc61416..0153cdbd68 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.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.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -24,11 +23,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private readonly IBindable direction = new Bindable(); private Container directionContainer = null!; - private Container keyIcon = null!; private Drawable background = null!; private Circle hitTargetLine = null!; + private Container bottomIcon = null!; + private CircularContainer topIcon = null!; + [Resolved] private Column column { get; set; } = null!; @@ -56,56 +57,58 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon RelativeSizeAxes = Axes.Both, Colour = column.AccentColour.Darken(0.6f), }, - keyIcon = new Container + hitTargetLine = new Circle + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = OsuColour.Gray(196 / 255f), + Height = 4, + Masking = true, + }, + new Container { Name = "Icons", RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Children = new[] + Children = new Drawable[] { - hitTargetLine = new Circle() + bottomIcon = new Container { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, - Colour = OsuColour.Gray(196 / 255f), - Height = 4, - Masking = true, - }, - new Circle - { + Blending = BlendingParameters.Additive, + Colour = column.AccentColour, Y = icon_vertical_offset, - Size = new Vector2(icon_circle_size), - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Blending = BlendingParameters.Additive, - Colour = column.AccentColour, - Masking = true, + Children = new[] + { + new Circle + { + Size = new Vector2(icon_circle_size), + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + }, + new Circle + { + X = -icon_spacing, + Y = icon_spacing * 1.2f, + Size = new Vector2(icon_circle_size), + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + }, + new Circle + { + X = icon_spacing, + Y = icon_spacing * 1.2f, + Size = new Vector2(icon_circle_size), + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + }, + } }, - new Circle - { - X = -icon_spacing, - Y = icon_vertical_offset + icon_spacing * 1.2f, - Size = new Vector2(icon_circle_size), - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Blending = BlendingParameters.Additive, - Colour = column.AccentColour, - Masking = true, - }, - new Circle - { - X = icon_spacing, - Y = icon_vertical_offset + icon_spacing * 1.2f, - Size = new Vector2(icon_circle_size), - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Blending = BlendingParameters.Additive, - Colour = column.AccentColour, - Masking = true, - }, - new CircularContainer + topIcon = new CircularContainer { Anchor = Anchor.TopCentre, Origin = Anchor.Centre, @@ -153,29 +156,41 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon public bool OnPressed(KeyBindingPressEvent e) { - if (e.Action == column.Action.Value) + if (e.Action != column.Action.Value) return false; + + const double lighting_fade_in_duration = 50; + Color4 lightingColour = column.AccentColour.Lighten(0.9f); + + background + .FadeColour(column.AccentColour.Lighten(0.4f), 40).Then() + .FadeColour(column.AccentColour, 150, Easing.OutQuint); + + hitTargetLine.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint); + hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { - background - .FadeColour(column.AccentColour.Lighten(0.3f), 50, Easing.OutQuint).Then() - .FadeColour(column.AccentColour, 100, Easing.OutQuint); + Type = EdgeEffectType.Glow, + Colour = lightingColour.Opacity(0.7f), + Radius = 20, + }, lighting_fade_in_duration, Easing.OutQuint); - foreach (var circle in keyIcon.Children.OfType()) + topIcon.ScaleTo(0.9f, lighting_fade_in_duration, Easing.OutQuint); + topIcon.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = lightingColour.Opacity(0.1f), + Radius = 20, + }, lighting_fade_in_duration, Easing.OutQuint); + + bottomIcon.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint); + + foreach (var circle in bottomIcon) + { + circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { - if (circle != hitTargetLine) - circle.ScaleTo(0.9f, 50, Easing.OutQuint); - - circle.FadeColour(Color4.White, 50, Easing.OutQuint); - - // TODO: VERY TMPOERAOIRY. - float f = circle == hitTargetLine ? 0.2f : (circle is Circle ? 0.05f : 0.2f); - - circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Color4.White.Opacity(f), - Radius = 40, - }, 50, Easing.OutQuint); - } + Type = EdgeEffectType.Glow, + Colour = lightingColour.Opacity(0.3f), + Radius = 60, + }, lighting_fade_in_duration, Easing.OutQuint); } return false; @@ -183,29 +198,39 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon public void OnReleased(KeyBindingReleaseEvent e) { - if (e.Action == column.Action.Value) + if (e.Action != column.Action.Value) return; + + const double lighting_fade_out_duration = 300; + Color4 lightingColour = column.AccentColour.Lighten(0.9f).Opacity(0); + + background.FadeColour(column.AccentColour.Darken(0.6f), lighting_fade_out_duration, Easing.OutQuint); + + topIcon.ScaleTo(1f, 200, Easing.OutQuint); + topIcon.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { - background.FadeColour(column.AccentColour.Darken(0.6f), 800, Easing.OutQuint); + Type = EdgeEffectType.Glow, + Colour = lightingColour, + Radius = 20, + }, lighting_fade_out_duration, Easing.OutQuint); - foreach (var circle in keyIcon.Children.OfType()) + hitTargetLine.FadeColour(OsuColour.Gray(196 / 255f), lighting_fade_out_duration, Easing.OutQuint); + hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = lightingColour, + Radius = 30, + }, lighting_fade_out_duration, Easing.OutQuint); + + bottomIcon.FadeColour(column.AccentColour, lighting_fade_out_duration, Easing.OutQuint); + + foreach (var circle in bottomIcon) + { + circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { - circle.ScaleTo(1f, 200, Easing.OutQuint); - - // TODO: temp lol - if (circle == hitTargetLine) - { - circle.FadeColour(OsuColour.Gray(196 / 255f), 800, Easing.OutQuint); - } - else if (circle is Circle) - circle.FadeColour(column.AccentColour, 800, Easing.OutQuint); - - circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Color4.White.Opacity(0), - Radius = 30, - }, 800, Easing.OutQuint); - } + Type = EdgeEffectType.Glow, + Colour = lightingColour, + Radius = 30, + }, lighting_fade_out_duration, Easing.OutQuint); } } } From 15d159a97e05b80445bc2e50161b448d827f96d4 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 4 Oct 2022 16:10:18 +0900 Subject: [PATCH 131/295] Fix tests --- .../Objects/Drawables/DrawableHoldNoteHead.cs | 8 -------- .../Visual/Gameplay/TestSceneScrollingHitObjects.cs | 6 ++++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs index d374e935ec..66cc93b033 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs @@ -30,14 +30,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public bool UpdateResult() => base.UpdateResult(true); - protected override void UpdateInitialTransforms() - { - base.UpdateInitialTransforms(); - - // This hitobject should never expire, so this is just a safe maximum. - LifetimeEnd = LifetimeStart + 30000; - } - protected override void UpdateHitStateTransforms(ArmedState state) { // suppress the base call explicitly. diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs index 98afbe92b1..156a1ee34a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs @@ -340,8 +340,10 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override RectangleF GetConservativeBoundingBox(HitObjectLifetimeEntry entry) { - var hitObject = (TestHitObject)entry.HitObject; - return new RectangleF().Inflate(hitObject.Size / 2); + if (entry.HitObject is TestHitObject testObject) + return new RectangleF().Inflate(testObject.Size / 2); + + return base.GetConservativeBoundingBox(entry); } } } From 345430ab39d45f7d47231b080472bb660c364e0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 16:15:24 +0900 Subject: [PATCH 132/295] Fix argon hit target area not being aligned correctly --- .../Skinning/Argon/ArgonHitTarget.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs index 6518607c9d..4750118583 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs @@ -2,18 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Mania.Skinning.Default; +using osu.Game.Rulesets.UI.Scrolling; using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Skinning.Argon { public class ArgonHitTarget : CompositeDrawable { + private readonly IBindable direction = new Bindable(); + [BackgroundDependencyLoader] - private void load() + private void load(IScrollingInfo scrollingInfo) { RelativeSizeAxes = Axes.X; Height = DefaultNotePiece.NOTE_HEIGHT; @@ -28,6 +32,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Colour = Color4.White }, }; + + direction.BindTo(scrollingInfo.Direction); + direction.BindValueChanged(onDirectionChanged, true); + } + + private void onDirectionChanged(ValueChangedEvent direction) + { + Anchor = Origin = direction.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; } } } From b0a948df459440dc7aefcf661e446a3eefe732bd Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 4 Oct 2022 00:17:00 -0700 Subject: [PATCH 133/295] Move `LegacySmoke` animation implementation to `Smoke` --- WeatherConfig.dat | 3 + .../Skinning/Default/DefaultSmoke.cs | 52 ------- .../Skinning/Legacy/LegacySmoke.cs | 144 +----------------- .../Legacy/OsuLegacySkinTransformer.cs | 2 +- osu.Game.Rulesets.Osu/Skinning/Smoke.cs | 127 +++++++++++++-- 5 files changed, 121 insertions(+), 207 deletions(-) create mode 100644 WeatherConfig.dat diff --git a/WeatherConfig.dat b/WeatherConfig.dat new file mode 100644 index 0000000000..88a312bf91 --- /dev/null +++ b/WeatherConfig.dat @@ -0,0 +1,3 @@ +5363943 +La Jolla,US +F diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index 65ae490882..c3bc04a858 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -1,65 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using osu.Framework.Graphics; -using osuTK; -using osuTK.Graphics; - namespace osu.Game.Rulesets.Osu.Skinning.Default { public class DefaultSmoke : Smoke { - private const double fade_out_delay = 8000; - private const double fade_out_speed = 3; - private const double fade_out_duration = 50; - private const float alpha = 0.5f; - - public override double LifetimeEnd => SmokeEndTime + fade_out_delay + fade_out_duration + (SmokeEndTime - SmokeStartTime) / fade_out_speed; - public DefaultSmoke() { Radius = 2; } - - protected override DrawNode CreateDrawNode() => new DefaultSmokeDrawNode(this); - - private class DefaultSmokeDrawNode : SmokeDrawNode - { - private double fadeOutTime; - - public DefaultSmokeDrawNode(ITexturedShaderDrawable source) - : base(source) - { - } - - public override void ApplyState() - { - base.ApplyState(); - - fadeOutTime = SmokeStartTime + fade_out_speed * (CurrentTime - (SmokeEndTime + fade_out_delay)); - } - - protected override Color4 PointColour(SmokePoint point) - { - var color = Color4.White; - color.A = alpha; - - double timeDoingFadeOut = fadeOutTime - point.Time; - - if (timeDoingFadeOut > 0) - { - float fraction = Math.Clamp((float)(1 - (timeDoingFadeOut / fade_out_duration)), 0, 1); - fraction = MathF.Pow(fraction, 5); - color.A *= fraction; - } - - return color; - } - - protected override float PointScale(SmokePoint point) => 1f; - - protected override Vector2 PointDirection(SmokePoint point) => point.Direction; - } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs index b8d70c1c6d..89e90cd4c8 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs @@ -1,157 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using osu.Framework.Graphics; -using osu.Framework.Utils; +using osu.Framework.Allocation; using osu.Game.Skinning; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacySmoke : Smoke { - // fade values - private const double initial_fade_out_duration = 4000; - - private const double re_fade_in_speed = 3; - private const double re_fade_in_duration = 50; - - private const double final_fade_out_speed = 2; - private const double final_fade_out_duration = 8000; - - private const float initial_alpha = 0.6f; - private const float re_fade_in_alpha = 1f; - - // scale values - private const double scale_duration = 1200; - - private const float initial_scale = 0.65f; - private const float final_scale = 1f; - - // rotation values - private const double rotation_duration = 500; - - private const float max_rotation = 0.25f; - - protected int RotationSeed { get; set; } = RNG.Next(); - - public override double LifetimeEnd - { - get - { - double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); - return SmokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; - } - } - - private readonly ISkin skin; - - public LegacySmoke(ISkin skin) - { - this.skin = skin; - } - - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load(ISkinSource skin) { base.LoadComplete(); Texture = skin.GetTexture("cursor-smoke"); } - - protected override DrawNode CreateDrawNode() => new LegacySmokeDrawNode(this); - - protected class LegacySmokeDrawNode : SmokeDrawNode - { - protected new LegacySmoke Source => (LegacySmoke)base.Source; - - private double initialFadeOutDurationTrunc; - private double firstVisiblePointTime; - - private double initialFadeOutTime; - private double reFadeInTime; - private double finalFadeOutTime; - - private int rotationSeed; - private Random rotationRNG = new Random(); - - public LegacySmokeDrawNode(ITexturedShaderDrawable source) - : base(source) - { - } - - public override void ApplyState() - { - base.ApplyState(); - - rotationSeed = Source.RotationSeed; - rotationRNG = new Random(rotationSeed); - - initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); - firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc; - - initialFadeOutTime = CurrentTime; - reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / re_fade_in_speed); - finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / final_fade_out_speed); - } - - protected override Color4 PointColour(SmokePoint point) - { - var color = Color4.White; - - double timeDoingInitialFadeOut = Math.Min(initialFadeOutTime, SmokeEndTime) - point.Time; - - if (timeDoingInitialFadeOut > 0) - { - float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1); - color.A = (1 - fraction) * initial_alpha; - } - - if (color.A > 0) - { - double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed; - double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed; - - if (timeDoingFinalFadeOut > 0) - { - float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1); - fraction = MathF.Pow(fraction, 5); - color.A = (1 - fraction) * re_fade_in_alpha; - } - else if (timeDoingReFadeIn > 0) - { - float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1); - fraction = 1 - MathF.Pow(1 - fraction, 5); - color.A = fraction * (re_fade_in_alpha - color.A) + color.A; - } - } - - return color; - } - - protected override float PointScale(SmokePoint point) - { - double timeDoingScale = CurrentTime - point.Time; - float fraction = Math.Clamp((float)(timeDoingScale / scale_duration), 0, 1); - fraction = 1 - MathF.Pow(1 - fraction, 5); - return fraction * (final_scale - initial_scale) + initial_scale; - } - - protected override Vector2 PointDirection(SmokePoint point) - { - float initialAngle = MathF.Atan2(point.Direction.Y, point.Direction.X); - float finalAngle = initialAngle + nextRotation(); - - double timeDoingRotation = CurrentTime - point.Time; - float fraction = Math.Clamp((float)(timeDoingRotation / rotation_duration), 0, 1); - fraction = 1 - MathF.Pow(1 - fraction, 5); - float angle = fraction * (finalAngle - initialAngle) + initialAngle; - - return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); - } - - private float nextRotation() => max_rotation * ((float)rotationRNG.NextDouble() * 2 - 1); - } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index a9186f821e..d44d2b031a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.Smoke: if (GetTexture("cursor-smoke") != null) - return new LegacySmoke(this); + return new LegacySmoke(); return null; diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs index fae875eedb..5d2aa2b1f6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Smoke.cs @@ -25,6 +25,15 @@ namespace osu.Game.Rulesets.Osu.Skinning public IShader? TextureShader { get; private set; } public IShader? RoundedTextureShader { get; private set; } + public override double LifetimeEnd + { + get + { + double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); + return SmokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; + } + } + private float? radius; protected float Radius @@ -39,21 +48,45 @@ namespace osu.Game.Rulesets.Osu.Skinning protected double SmokeEndTime { get; private set; } = double.MaxValue; - protected virtual float PointInterval => Radius * 7f / 8; - protected readonly List SmokePoints = new List(); + protected virtual float PointInterval => Radius * 7f / 8; + private float totalDistance; private Vector2? lastPosition; + private SmokeContainer? smokeContainer; private const int max_point_count = 18_000; - [Resolved(CanBeNull = true)] - private SmokeContainer? smokeContainer { get; set; } + // fade anim values + private const double initial_fade_out_duration = 4000; + + private const double re_fade_in_speed = 3; + private const double re_fade_in_duration = 50; + + private const double final_fade_out_speed = 2; + private const double final_fade_out_duration = 8000; + + private const float initial_alpha = 0.6f; + private const float re_fade_in_alpha = 1f; + + private readonly int rotationSeed = RNG.Next(); + + // scale anim values + private const double scale_duration = 1200; + + private const float initial_scale = 0.65f; + private const float final_scale = 1f; + + // rotation anim values + private const double rotation_duration = 500; + + private const float max_rotation = 0.25f; [BackgroundDependencyLoader] - private void load(ShaderManager shaders) + private void load(SmokeContainer container, ShaderManager shaders) { + smokeContainer = container; RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE); } @@ -128,8 +161,6 @@ namespace osu.Game.Rulesets.Osu.Skinning onSmokeEnded(time); } - public abstract override double LifetimeEnd { get; } - private void onSmokeEnded(double time) { if (smokeContainer != null) @@ -141,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokeEndTime = time; } - protected abstract override DrawNode CreateDrawNode(); + protected override DrawNode CreateDrawNode() => new SmokeDrawNode(this); protected override void Update() { @@ -181,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } } - protected abstract class SmokeDrawNode : TexturedShaderDrawNode + protected class SmokeDrawNode : TexturedShaderDrawNode { protected new Smoke Source => (Smoke)base.Source; @@ -195,7 +226,17 @@ namespace osu.Game.Rulesets.Osu.Skinning private Vector2 drawSize; private Texture? texture; - protected SmokeDrawNode(ITexturedShaderDrawable source) + // anim calculation vars (color, scale, direction) + private double initialFadeOutDurationTrunc; + private double firstVisiblePointTime; + + private double initialFadeOutTime; + private double reFadeInTime; + private double finalFadeOutTime; + + private Random rotationRNG = new Random(); + + public SmokeDrawNode(ITexturedShaderDrawable source) : base(source) { } @@ -214,6 +255,15 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokeStartTime = Source.SmokeStartTime; SmokeEndTime = Source.SmokeEndTime; CurrentTime = Source.Clock.CurrentTime; + + rotationRNG = new Random(Source.rotationSeed); + + initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); + firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc; + + initialFadeOutTime = CurrentTime; + reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / re_fade_in_speed); + finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / final_fade_out_speed); } public sealed override void Draw(IRenderer renderer) @@ -246,11 +296,62 @@ namespace osu.Game.Rulesets.Osu.Skinning ? ((SRGBColour)DrawColourInfo.Colour).Linear : DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, drawSize)).Linear; - protected abstract Color4 PointColour(SmokePoint point); + protected virtual Color4 PointColour(SmokePoint point) + { + var color = Color4.White; - protected abstract float PointScale(SmokePoint point); + double timeDoingInitialFadeOut = Math.Min(initialFadeOutTime, SmokeEndTime) - point.Time; - protected abstract Vector2 PointDirection(SmokePoint point); + if (timeDoingInitialFadeOut > 0) + { + float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1); + color.A = (1 - fraction) * initial_alpha; + } + + if (color.A > 0) + { + double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed; + double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed; + + if (timeDoingFinalFadeOut > 0) + { + float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1); + fraction = MathF.Pow(fraction, 5); + color.A = (1 - fraction) * re_fade_in_alpha; + } + else if (timeDoingReFadeIn > 0) + { + float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1); + fraction = 1 - MathF.Pow(1 - fraction, 5); + color.A = fraction * (re_fade_in_alpha - color.A) + color.A; + } + } + + return color; + } + + protected virtual float PointScale(SmokePoint point) + { + double timeDoingScale = CurrentTime - point.Time; + float fraction = Math.Clamp((float)(timeDoingScale / scale_duration), 0, 1); + fraction = 1 - MathF.Pow(1 - fraction, 5); + return fraction * (final_scale - initial_scale) + initial_scale; + } + + protected virtual Vector2 PointDirection(SmokePoint point) + { + float initialAngle = MathF.Atan2(point.Direction.Y, point.Direction.X); + float finalAngle = initialAngle + nextRotation(); + + double timeDoingRotation = CurrentTime - point.Time; + float fraction = Math.Clamp((float)(timeDoingRotation / rotation_duration), 0, 1); + fraction = 1 - MathF.Pow(1 - fraction, 5); + float angle = fraction * (finalAngle - initialAngle) + initialAngle; + + return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); + } + + private float nextRotation() => max_rotation * ((float)rotationRNG.NextDouble() * 2 - 1); private void drawPointQuad(SmokePoint point, RectangleF textureRect) { From 395ab5889220b3d705441871904cbbfe5b7ca907 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 16:17:38 +0900 Subject: [PATCH 134/295] Attempt to fix code style infractions --- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 3 +-- .../Skinning/Argon/OsuArgonSkinTransformer.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index dab71a65c3..ec6aaf2ef7 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon switch (component) { case ManiaSkinComponent maniaComponent: + // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. switch (maniaComponent.Component) { case ManiaSkinComponents.HitTarget: @@ -25,8 +26,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case ManiaSkinComponents.KeyArea: return new ArgonKeyArea(); - - // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. } break; diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs index 3794350f6a..bf507db50c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon return new ArgonJudgementPiece(resultComponent.Component); case OsuSkinComponent osuComponent: + // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. switch (osuComponent.Component) { case OsuSkinComponents.HitCircle: @@ -56,8 +57,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon case OsuSkinComponents.CursorTrail: return new ArgonCursorTrail(); - - // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. } break; From 33f33a684571833115a3e0bd581bbc34f1e253d4 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 4 Oct 2022 00:53:03 -0700 Subject: [PATCH 135/295] Remove things that should've been removed before push --- WeatherConfig.dat | 3 --- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 WeatherConfig.dat diff --git a/WeatherConfig.dat b/WeatherConfig.dat deleted file mode 100644 index 88a312bf91..0000000000 --- a/WeatherConfig.dat +++ /dev/null @@ -1,3 +0,0 @@ -5363943 -La Jolla,US -F diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index e139e16ff2..4984dc1ad1 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Logging; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; @@ -31,8 +30,6 @@ namespace osu.Game.Rulesets.Osu.UI { if (e.Action == OsuAction.Smoke) { - Logger.Log("holy moly"); - isSmoking = true; AddInternal(new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Smoke), _ => new DefaultSmoke())); From c2956c6e1e9e64a1154040550e36ef5042f7adbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 17:28:15 +0900 Subject: [PATCH 136/295] Add osu! hit object dim Stable would dim objects when they can't be hit (ie. the "miss" window is not active yet). This was never implemented in lazer, and causes quite large visual differences. No one has mentioned this yet, but it will definitely be one of those missing pieces which makes lazer feel different to stable. --- .../Objects/Drawables/DrawableOsuHitObject.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 6f4ca30bd0..26518e0a27 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.Scoring; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -64,6 +66,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ScaleBindable.UnbindFrom(HitObject.ScaleBindable); } + protected override void UpdateInitialTransforms() + { + base.UpdateInitialTransforms(); + + double missWindow = HitObject.HitWindows.WindowFor(HitResult.Miss); + + // 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 - missWindow)) + this.FadeColour(Color4.White, 100); + } + protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt; private OsuInputManager osuActionInputManager; From 41082ab9286921be507a3b4a82d0cc95e69b9438 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 4 Oct 2022 18:21:29 +0900 Subject: [PATCH 137/295] Fix misplaced parenthesis --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6359ed3e27..939d3a63ed 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -565,7 +565,7 @@ namespace osu.Game // 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); From 7293ad751ee92a3d8d3d4f5dd95d6f2260794c69 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 4 Oct 2022 18:59:33 +0900 Subject: [PATCH 138/295] Update packages --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 77c29a5d6e..c33937ad95 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 29e690a024..31b17fb8e1 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 83410b08f6..a9a83e2802 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -82,7 +82,7 @@ - + From 44b99444a7720533a3240dc174a3d4165ff893b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 13:42:04 +0900 Subject: [PATCH 139/295] Hide approach circles immediate on successful hit --- .../Objects/Drawables/DrawableHitCircle.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index c5992b359d..23db29b9a6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -204,12 +204,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // todo: temporary / arbitrary, used for lifetime optimisation. this.Delay(800).FadeOut(); - // in the case of an early state change, the fade should be expedited to the current point in time. - if (HitStateUpdateTime < HitObject.StartTime) - ApproachCircle.FadeOut(50); - switch (state) { + default: + ApproachCircle.FadeOut(); + break; + case ArmedState.Idle: HitArea.HitAction = null; break; From e06ece7531c11c4f9a8717b1bb5f70338a27fbc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 14:15:54 +0900 Subject: [PATCH 140/295] Update framework --- osu.Android.props | 2 +- osu.Desktop/OsuGameDesktop.cs | 7 ++++--- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c33937ad95..6ad810483b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + 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/osu.Game.csproj b/osu.Game/osu.Game.csproj index 31b17fb8e1..df7bfab17a 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 a9a83e2802..9e2568bf7e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -82,7 +82,7 @@ - + From 276021dd85f2abe7774f5a91e474e0f7307b55fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 14:20:01 +0900 Subject: [PATCH 141/295] Remove now unnecessary `ScheduleAfterChildren` --- osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 548dd2a5bb..2eec8253b3 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -335,8 +335,7 @@ namespace osu.Game.Screens.Play.HUD private float? scoreComponentsTargetWidth; - // Schedule required to get correct DrawWidth from text after updates. - private void updateDetailsWidth() => SchedulerAfterChildren.AddOnce(() => + private void updateDetailsWidth() { const float score_components_min_width = 88f; @@ -349,7 +348,7 @@ namespace osu.Game.Screens.Play.HUD scoreComponentsTargetWidth = newWidth; scoreComponents.ResizeWidthTo(newWidth, panel_transition_duration, Easing.OutQuint); - }); + } private void updateState() { From 56d424003d240863894829e9241ad1b9b18a290a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 14:25:04 +0900 Subject: [PATCH 142/295] Fix sliders not dimming correctly due to modified miss window --- .../Objects/Drawables/DrawableOsuHitObject.cs | 6 ++---- osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs | 9 ++++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 26518e0a27..2935791cde 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -11,7 +11,7 @@ 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.Scoring; +using osu.Game.Rulesets.Osu.Scoring; using osuTK; using osuTK.Graphics; @@ -70,14 +70,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateInitialTransforms(); - double missWindow = HitObject.HitWindows.WindowFor(HitResult.Miss); - // 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 - missWindow)) + using (BeginDelayedSequence(InitialLifetimeOffset - OsuHitWindows.MISS_WINDOW)) this.FadeColour(Color4.White, 100); } 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) From 74db42394a47e11c258abc8417d6076c2f850a66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 14:31:12 +0900 Subject: [PATCH 143/295] Silence unobserved exceptions in `BeginPlayingInternal` Closes #20526. --- osu.Game/Online/Spectator/OnlineSpectatorClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs index a012bf49b6..48d5c0bea9 100644 --- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs +++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Diagnostics; using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR.Client; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -58,7 +58,7 @@ namespace osu.Game.Online.Spectator { await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), state); } - catch (HubException exception) + catch (Exception exception) { if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE) { From 52002d91dd578efcd216930adab12546ab0c13d9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 17:48:56 +0900 Subject: [PATCH 144/295] Only apply dim at top level objects --- .../Objects/Drawables/DrawableOsuHitObject.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 2935791cde..d9d0d28477 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -70,13 +70,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateInitialTransforms(); - // 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); + // 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; From 04abb2ce8f1723fbf342d7cb215eea0d20d221bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:26:17 +0900 Subject: [PATCH 145/295] Update default cursor smoke implementation to use a texture --- osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs index c3bc04a858..1b7a39aa30 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs @@ -1,13 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Graphics.Textures; + namespace osu.Game.Rulesets.Osu.Skinning.Default { public class DefaultSmoke : Smoke { - public DefaultSmoke() + [BackgroundDependencyLoader] + private void load(TextureStore textures) { - Radius = 2; + // ISkinSource doesn't currently fallback to global textures. + // We might want to change this in the future if the intention is to allow the user to skin this as per legacy skins. + Texture = textures.Get("Gameplay/osu/cursor-smoke"); } } } From 1e5ff2679b3ba6b00b6e858561f5707e85cdd56d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:27:51 +0900 Subject: [PATCH 146/295] Rename classes to better emphasise that `Smoke` is a single trail segment --- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 2 +- .../Default/{DefaultSmoke.cs => DefaultSmokeSegment.cs} | 2 +- .../Legacy/{LegacySmoke.cs => LegacySmokeSegment.cs} | 2 +- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 4 ++-- osu.Game.Rulesets.Osu/Skinning/{Smoke.cs => SmokeSegment.cs} | 4 ++-- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 5 ++++- 6 files changed, 11 insertions(+), 8 deletions(-) rename osu.Game.Rulesets.Osu/Skinning/Default/{DefaultSmoke.cs => DefaultSmokeSegment.cs} (92%) rename osu.Game.Rulesets.Osu/Skinning/Legacy/{LegacySmoke.cs => LegacySmokeSegment.cs} (90%) rename osu.Game.Rulesets.Osu/Skinning/{Smoke.cs => SmokeSegment.cs} (99%) diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index 11daa26072..3ee30ff7dc 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu SliderBall, SliderBody, SpinnerBody, - Smoke, + SmokeTrail, ApproachCircle, } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmokeSegment.cs similarity index 92% rename from osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs rename to osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmokeSegment.cs index 1b7a39aa30..27a2dc3960 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSmokeSegment.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Textures; namespace osu.Game.Rulesets.Osu.Skinning.Default { - public class DefaultSmoke : Smoke + public class DefaultSmokeSegment : SmokeSegment { [BackgroundDependencyLoader] private void load(TextureStore textures) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmokeSegment.cs similarity index 90% rename from osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmokeSegment.cs index 89e90cd4c8..c9c7e86e86 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmokeSegment.cs @@ -6,7 +6,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacySmoke : Smoke + public class LegacySmokeSegment : SmokeSegment { [BackgroundDependencyLoader] private void load(ISkinSource skin) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index d44d2b031a..4d12b85770 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -106,9 +106,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; - case OsuSkinComponents.Smoke: + case OsuSkinComponents.SmokeTrail: if (GetTexture("cursor-smoke") != null) - return new LegacySmoke(); + return new LegacySmokeSegment(); return null; diff --git a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs similarity index 99% rename from osu.Game.Rulesets.Osu/Skinning/Smoke.cs rename to osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 5d2aa2b1f6..948ec972a3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Smoke.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -20,7 +20,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning { - public abstract class Smoke : Drawable, ITexturedShaderDrawable + public abstract class SmokeSegment : Drawable, ITexturedShaderDrawable { public IShader? TextureShader { get; private set; } public IShader? RoundedTextureShader { get; private set; } @@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Skinning protected class SmokeDrawNode : TexturedShaderDrawNode { - protected new Smoke Source => (Smoke)base.Source; + protected new SmokeSegment Source => (SmokeSegment)base.Source; protected double SmokeStartTime { get; private set; } protected double SmokeEndTime { get; private set; } diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index 4984dc1ad1..d26d45c3d7 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -14,6 +14,9 @@ using osuTK; namespace osu.Game.Rulesets.Osu.UI { + /// + /// Manages smoke trails generated from user input. + /// [Cached] public class SmokeContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler { @@ -31,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.UI if (e.Action == OsuAction.Smoke) { isSmoking = true; - AddInternal(new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Smoke), _ => new DefaultSmoke())); + AddInternal(new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SmokeTrail), _ => new DefaultSmokeSegment())); return true; } From 6628ab5190409e93dd4fb46f57ac0877c1d27905 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:37:09 +0900 Subject: [PATCH 147/295] Refactor to avoid DI / event flow There's always one active smoke segment and it's the direct child of `SmokeContainer`. This can be simplified as such. --- .../Skinning/SmokeSegment.cs | 36 +++---------------- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 25 ++++++------- 2 files changed, 14 insertions(+), 47 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 948ec972a3..245026dc0b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; -using osu.Game.Rulesets.Osu.UI; using osuTK; using osuTK.Graphics; @@ -54,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private float totalDistance; private Vector2? lastPosition; - private SmokeContainer? smokeContainer; private const int max_point_count = 18_000; @@ -84,9 +82,8 @@ namespace osu.Game.Rulesets.Osu.Skinning private const float max_rotation = 0.25f; [BackgroundDependencyLoader] - private void load(SmokeContainer container, ShaderManager shaders) + private void load(ShaderManager shaders) { - smokeContainer = container; RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE); } @@ -100,14 +97,6 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokeStartTime = Time.Current; totalDistance = PointInterval; - - if (smokeContainer != null) - { - smokeContainer.SmokeMoved += onSmokeMoved; - smokeContainer.SmokeEnded += onSmokeEnded; - - onSmokeMoved(smokeContainer.LastMousePosition, Time.Current); - } } private Vector2 nextPointDirection() @@ -116,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Skinning return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); } - private void onSmokeMoved(Vector2 position, double time) + public void AddPosition(Vector2 position, double time) { lastPosition ??= position; @@ -158,17 +147,11 @@ namespace osu.Game.Rulesets.Osu.Skinning lastPosition = position; if (SmokePoints.Count >= max_point_count) - onSmokeEnded(time); + FinishDrawing(time); } - private void onSmokeEnded(double time) + public void FinishDrawing(double time) { - if (smokeContainer != null) - { - smokeContainer.SmokeMoved -= onSmokeMoved; - smokeContainer.SmokeEnded -= onSmokeEnded; - } - SmokeEndTime = time; } @@ -181,17 +164,6 @@ namespace osu.Game.Rulesets.Osu.Skinning Invalidate(Invalidation.DrawNode); } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (smokeContainer != null) - { - smokeContainer.SmokeMoved -= onSmokeMoved; - smokeContainer.SmokeEnded -= onSmokeEnded; - } - } - protected struct SmokePoint { public Vector2 Position; diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index d26d45c3d7..c00493e087 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -1,13 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; @@ -17,15 +16,11 @@ namespace osu.Game.Rulesets.Osu.UI /// /// Manages smoke trails generated from user input. /// - [Cached] public class SmokeContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler { - public event Action? SmokeMoved; - public event Action? SmokeEnded; - public Vector2 LastMousePosition; - private bool isSmoking; + private SkinnableDrawable? currentSegment; public override bool ReceivePositionalInputAt(Vector2 _) => true; @@ -33,21 +28,22 @@ namespace osu.Game.Rulesets.Osu.UI { if (e.Action == OsuAction.Smoke) { - isSmoking = true; - AddInternal(new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SmokeTrail), _ => new DefaultSmokeSegment())); - + AddInternal(currentSegment = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SmokeTrail), _ => new DefaultSmokeSegment())); + addPosition(LastMousePosition, Time.Current); return true; } return false; } + private void addPosition(Vector2 position, double timeCurrent) => (currentSegment?.Drawable as SmokeSegment)?.AddPosition(position, timeCurrent); + public void OnReleased(KeyBindingReleaseEvent e) { if (e.Action == OsuAction.Smoke) { - isSmoking = false; - SmokeEnded?.Invoke(Time.Current); + (currentSegment?.Drawable as SmokeSegment)?.FinishDrawing(Time.Current); + currentSegment = null; foreach (Drawable child in Children) { @@ -59,11 +55,10 @@ namespace osu.Game.Rulesets.Osu.UI protected override bool OnMouseMove(MouseMoveEvent e) { - if (isSmoking) - SmokeMoved?.Invoke(e.MousePosition, Time.Current); + if (currentSegment != null) + addPosition(e.MousePosition, Time.Current); LastMousePosition = e.MousePosition; - return base.OnMouseMove(e); } } From 71edd314b1785106be7b55c3ed638c6571d70521 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:51:02 +0900 Subject: [PATCH 148/295] Simplify `SmokeContainer` lifetime logic --- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index c00493e087..5999abc1bf 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.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.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -18,9 +17,9 @@ namespace osu.Game.Rulesets.Osu.UI /// public class SmokeContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler { - public Vector2 LastMousePosition; + private SkinnableDrawable? currentSegmentSkinnable; - private SkinnableDrawable? currentSegment; + private Vector2 lastMousePosition; public override bool ReceivePositionalInputAt(Vector2 _) => true; @@ -28,38 +27,38 @@ namespace osu.Game.Rulesets.Osu.UI { if (e.Action == OsuAction.Smoke) { - AddInternal(currentSegment = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SmokeTrail), _ => new DefaultSmokeSegment())); - addPosition(LastMousePosition, Time.Current); + AddInternal(currentSegmentSkinnable = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SmokeTrail), _ => new DefaultSmokeSegment())); + + // Add initial position immediately. + addPosition(); return true; } return false; } - private void addPosition(Vector2 position, double timeCurrent) => (currentSegment?.Drawable as SmokeSegment)?.AddPosition(position, timeCurrent); - public void OnReleased(KeyBindingReleaseEvent e) { if (e.Action == OsuAction.Smoke) { - (currentSegment?.Drawable as SmokeSegment)?.FinishDrawing(Time.Current); - currentSegment = null; - - foreach (Drawable child in Children) + if (currentSegmentSkinnable?.Drawable is SmokeSegment segment) { - var skinnable = (SkinnableDrawable)child; - skinnable.LifetimeEnd = skinnable.Drawable.LifetimeEnd; + segment.FinishDrawing(Time.Current); + + currentSegmentSkinnable.LifetimeEnd = segment.LifetimeEnd; + currentSegmentSkinnable = null; } } } protected override bool OnMouseMove(MouseMoveEvent e) { - if (currentSegment != null) - addPosition(e.MousePosition, Time.Current); + lastMousePosition = e.MousePosition; + addPosition(); - LastMousePosition = e.MousePosition; return base.OnMouseMove(e); } + + private void addPosition() => (currentSegmentSkinnable?.Drawable as SmokeSegment)?.AddPosition(lastMousePosition, Time.Current); } } From 91d877e8931da7e4d738a57189dbb5a1e4b60902 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:52:01 +0900 Subject: [PATCH 149/295] Set `LifetimeEnd` once rather than computing on every access --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 245026dc0b..18e0bc5baa 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -24,15 +24,6 @@ namespace osu.Game.Rulesets.Osu.Skinning public IShader? TextureShader { get; private set; } public IShader? RoundedTextureShader { get; private set; } - public override double LifetimeEnd - { - get - { - double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); - return SmokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; - } - } - private float? radius; protected float Radius @@ -153,6 +144,9 @@ namespace osu.Game.Rulesets.Osu.Skinning public void FinishDrawing(double time) { SmokeEndTime = time; + + double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); + LifetimeEnd = SmokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; } protected override DrawNode CreateDrawNode() => new SmokeDrawNode(this); From bd82dfc333cce808d06df5001c9e28ba387c589d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:53:07 +0900 Subject: [PATCH 150/295] Remove custom radius implementation --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 18e0bc5baa..09cbc1af1d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -24,13 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning public IShader? TextureShader { get; private set; } public IShader? RoundedTextureShader { get; private set; } - private float? radius; - - protected float Radius - { - get => radius ?? Texture?.DisplayWidth * 0.165f ?? 3; - set => radius = value; - } + protected float Radius => Texture?.DisplayWidth * 0.165f ?? 3; protected Texture? Texture { get; set; } From 8f0ef99e10670d92855848f0318ca1b98eee8db1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:54:14 +0900 Subject: [PATCH 151/295] Privatise some fields --- .../Skinning/SmokeSegment.cs | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 09cbc1af1d..99196f6967 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -21,24 +21,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { public abstract class SmokeSegment : Drawable, ITexturedShaderDrawable { - public IShader? TextureShader { get; private set; } - public IShader? RoundedTextureShader { get; private set; } - - protected float Radius => Texture?.DisplayWidth * 0.165f ?? 3; - - protected Texture? Texture { get; set; } - - protected double SmokeStartTime { get; private set; } = double.MinValue; - - protected double SmokeEndTime { get; private set; } = double.MaxValue; - - protected readonly List SmokePoints = new List(); - - protected virtual float PointInterval => Radius * 7f / 8; - - private float totalDistance; - private Vector2? lastPosition; - private const int max_point_count = 18_000; // fade anim values @@ -66,6 +48,24 @@ namespace osu.Game.Rulesets.Osu.Skinning private const float max_rotation = 0.25f; + public IShader? TextureShader { get; private set; } + public IShader? RoundedTextureShader { get; private set; } + + protected Texture? Texture { get; set; } + + private float radius => Texture?.DisplayWidth * 0.165f ?? 3; + + protected readonly List SmokePoints = new List(); + + private float pointInterval => radius * 7f / 8; + + private double smokeStartTime { get; set; } = double.MinValue; + + private double smokeEndTime { get; set; } = double.MaxValue; + + private float totalDistance; + private Vector2? lastPosition; + [BackgroundDependencyLoader] private void load(ShaderManager shaders) { @@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Osu.Skinning RelativeSizeAxes = Axes.Both; - SmokeStartTime = Time.Current; + smokeStartTime = Time.Current; - totalDistance = PointInterval; + totalDistance = pointInterval; } private Vector2 nextPointDirection() @@ -96,15 +96,15 @@ namespace osu.Game.Rulesets.Osu.Skinning float delta = (position - (Vector2)lastPosition).LengthFast; totalDistance += delta; - int count = (int)(totalDistance / PointInterval); + int count = (int)(totalDistance / pointInterval); if (count > 0) { Vector2 increment = position - (Vector2)lastPosition; increment.NormalizeFast(); - Vector2 pointPos = (PointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition; - increment *= PointInterval; + Vector2 pointPos = (pointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition; + increment *= pointInterval; if (SmokePoints.Count > 0 && SmokePoints[^1].Time > time) { @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokePoints.RemoveRange(index, SmokePoints.Count - index); } - totalDistance %= PointInterval; + totalDistance %= pointInterval; for (int i = 0; i < count; i++) { @@ -137,10 +137,10 @@ namespace osu.Game.Rulesets.Osu.Skinning public void FinishDrawing(double time) { - SmokeEndTime = time; + smokeEndTime = time; - double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); - LifetimeEnd = SmokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; + double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, smokeEndTime - smokeStartTime); + LifetimeEnd = smokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed; } protected override DrawNode CreateDrawNode() => new SmokeDrawNode(this); @@ -208,12 +208,12 @@ namespace osu.Game.Rulesets.Osu.Skinning points.Clear(); points.AddRange(Source.SmokePoints); - radius = Source.Radius; + radius = Source.radius; drawSize = Source.DrawSize; texture = Source.Texture; - SmokeStartTime = Source.SmokeStartTime; - SmokeEndTime = Source.SmokeEndTime; + SmokeStartTime = Source.smokeStartTime; + SmokeEndTime = Source.smokeEndTime; CurrentTime = Source.Clock.CurrentTime; rotationRNG = new Random(Source.rotationSeed); From 64858cfb8e2dabab010f9fab666cf1cf5b0ed9f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 18:55:49 +0900 Subject: [PATCH 152/295] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index fd57862c4d..8f83c9730b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9cc4f3fbab..434db87a80 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index cc922d304f..f395eab23c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From 234c6ac7998fbc6742503e1a589536255554e56a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 5 Oct 2022 20:21:15 +0900 Subject: [PATCH 153/295] Pin taiko PP calculator accuracy to osu-stable values --- .../Difficulty/TaikoPerformanceCalculator.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 95a1e8bc66..dc7bad2f75 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty private int countOk; private int countMeh; private int countMiss; + private double accuracy; private double effectiveMissCount; @@ -36,6 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty countOk = score.Statistics.GetValueOrDefault(HitResult.Ok); countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); + accuracy = customAccuracy; // The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000. if (totalSuccessfulHits > 0) @@ -87,7 +89,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (score.Mods.Any(m => m is ModFlashlight)) difficultyValue *= 1.050 * lengthBonus; - return difficultyValue * Math.Pow(score.Accuracy, 2.0); + return difficultyValue * Math.Pow(accuracy, 2.0); } private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes) @@ -95,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (attributes.GreatHitWindow <= 0) return 0; - double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0; + double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0; double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3)); accuracyValue *= lengthBonus; @@ -110,5 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; + + private double customAccuracy => totalHits > 0 ? (countGreat * 300 + countOk * 150) / (totalHits * 300.0) : 0; } } From 8d29e9e76bc781d8814e70a7a1f0317cfe7a2920 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 20:23:59 +0900 Subject: [PATCH 154/295] Move selection logic from DragBox to BlueprintContainer --- .../Compose/Components/BlueprintContainer.cs | 49 +++++++------------ .../Edit/Compose/Components/DragBox.cs | 46 +++-------------- .../Timeline/TimelineBlueprintContainer.cs | 12 +++-- .../Components/Timeline/TimelineDragBox.cs | 16 +----- 4 files changed, 34 insertions(+), 89 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 8b38d9c612..787959d214 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; @@ -13,11 +12,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -79,7 +76,7 @@ namespace osu.Game.Screens.Edit.Compose.Components AddRangeInternal(new[] { - DragBox = CreateDragBox(selectBlueprintsFromDragRectangle), + DragBox = CreateDragBox(), SelectionHandler, SelectionBlueprints = CreateSelectionBlueprintContainer(), SelectionHandler.CreateProxy(), @@ -101,7 +98,7 @@ namespace osu.Game.Screens.Edit.Compose.Components [CanBeNull] protected virtual SelectionBlueprint CreateBlueprintFor(T item) => null; - protected virtual DragBox CreateDragBox(Action performSelect) => new DragBox(performSelect); + protected virtual DragBox CreateDragBox() => new DragBox(); /// /// Whether this component is in a state where items outside a drag selection should be deselected. If false, selection will only be added to. @@ -183,13 +180,9 @@ namespace osu.Game.Screens.Edit.Compose.Components return true; } - if (DragBox.HandleDrag(e)) - { - DragBox.Show(); - return true; - } - - return false; + DragBox.HandleDrag(e); + DragBox.Show(); + return true; } protected override void OnDrag(DragEvent e) @@ -198,7 +191,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return; if (DragBox.State == Visibility.Visible) + { DragBox.HandleDrag(e); + UpdateSelectionFromDragBox(); + } moveCurrentSelection(e); } @@ -214,8 +210,7 @@ namespace osu.Game.Screens.Edit.Compose.Components changeHandler?.EndChange(); } - if (DragBox.State == Visibility.Visible) - DragBox.Hide(); + DragBox.Hide(); } /// @@ -380,28 +375,20 @@ namespace osu.Game.Screens.Edit.Compose.Components } /// - /// Select all masks in a given rectangle selection area. + /// Select all blueprints in a selection area specified by . /// - /// The rectangle to perform a selection on in screen-space coordinates. - private void selectBlueprintsFromDragRectangle(RectangleF rect) + protected virtual void UpdateSelectionFromDragBox() { + var quad = DragBox.Box.ScreenSpaceDrawQuad; + foreach (var blueprint in SelectionBlueprints) { - // only run when utmost necessary to avoid unnecessary rect computations. - bool isValidForSelection() => blueprint.IsAlive && blueprint.IsPresent && rect.Contains(blueprint.ScreenSpaceSelectionPoint); + if (blueprint.IsSelected && !AllowDeselectionDuringDrag) + continue; - switch (blueprint.State) - { - case SelectionState.NotSelected: - if (isValidForSelection()) - blueprint.Select(); - break; - - case SelectionState.Selected: - if (AllowDeselectionDuringDrag && !isValidForSelection()) - blueprint.Deselect(); - break; - } + bool shouldBeSelected = blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint); + if (blueprint.IsSelected != shouldBeSelected) + blueprint.ToggleSelection(); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index 838562719d..905d47533a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -8,7 +8,6 @@ using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Layout; @@ -21,18 +20,13 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public class DragBox : CompositeDrawable, IStateful { - protected readonly Action PerformSelection; - - protected Drawable Box; + public Drawable Box { get; private set; } /// /// Creates a new . /// - /// A delegate that performs drag selection. - public DragBox(Action performSelection) + public DragBox() { - PerformSelection = performSelection; - RelativeSizeAxes = Axes.Both; AlwaysPresent = true; Alpha = 0; @@ -46,30 +40,14 @@ namespace osu.Game.Screens.Edit.Compose.Components protected virtual Drawable CreateBox() => new BoxWithBorders(); - private RectangleF? dragRectangle; - /// /// Handle a forwarded mouse event. /// /// The mouse event. - /// Whether the event should be handled and blocking. - public virtual bool HandleDrag(MouseButtonEvent e) + public virtual void HandleDrag(MouseButtonEvent e) { - var dragPosition = e.ScreenSpaceMousePosition; - var dragStartPosition = e.ScreenSpaceMouseDownPosition; - - var dragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y); - - // We use AABBFloat instead of RectangleF since it handles negative sizes for us - var rec = dragQuad.AABBFloat; - dragRectangle = rec; - - var topLeft = ToLocalSpace(rec.TopLeft); - var bottomRight = ToLocalSpace(rec.BottomRight); - - Box.Position = topLeft; - Box.Size = bottomRight - topLeft; - return true; + Box.Position = Vector2.ComponentMin(e.MouseDownPosition, e.MousePosition); + Box.Size = Vector2.ComponentMax(e.MouseDownPosition, e.MousePosition) - Box.Position; } private Visibility state; @@ -87,19 +65,7 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - protected override void Update() - { - base.Update(); - - if (dragRectangle != null) - PerformSelection?.Invoke(dragRectangle.Value); - } - - public override void Hide() - { - State = Visibility.Hidden; - dragRectangle = null; - } + public override void Hide() => State = Visibility.Hidden; public override void Show() => State = Visibility.Visible; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 590f92d281..da80ed5ad6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -13,7 +13,6 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Utils; @@ -65,7 +64,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override void LoadComplete() { base.LoadComplete(); - DragBox.Alpha = 0; placement = Beatmap.PlacementObject.GetBoundCopy(); placement.ValueChanged += placementChanged; @@ -93,6 +91,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected override bool OnDragStart(DragStartEvent e) + { + if (!base.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition)) + return false; + + return base.OnDragStart(e); + } + protected override void OnDrag(DragEvent e) { handleScrollViaDrag(e); @@ -169,7 +175,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }; } - protected override DragBox CreateDragBox(Action performSelect) => new TimelineDragBox(performSelect); + protected override DragBox CreateDragBox() => new TimelineDragBox(); private void handleScrollViaDrag(DragEvent e) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs index c026c169d6..8b901c8958 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs @@ -6,7 +6,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Utils; @@ -24,24 +23,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private Timeline timeline { get; set; } - public TimelineDragBox(Action performSelect) - : base(performSelect) - { - } - protected override Drawable CreateBox() => new Box { RelativeSizeAxes = Axes.Y, Alpha = 0.3f }; - public override bool HandleDrag(MouseButtonEvent e) + public override void HandleDrag(MouseButtonEvent e) { - // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. - float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y; - if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY) - return false; - selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; // only calculate end when a transition is not in progress to avoid bouncing. @@ -49,7 +38,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline selectionEnd = e.MousePosition.X / timeline.CurrentZoom; updateDragBoxPosition(); - return true; } private void updateDragBoxPosition() @@ -68,8 +56,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment. boxScreenRect.Y -= boxScreenRect.Height; boxScreenRect.Height *= 2; - - PerformSelection?.Invoke(boxScreenRect); } public override void Hide() From 0ffde02f79e8d67538e941e312abfa1611e5e712 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 20:43:02 +0900 Subject: [PATCH 155/295] Use hit object time for timeline selection --- .../Compose/Components/Timeline/Timeline.cs | 18 ++++++-- .../Timeline/TimelineBlueprintContainer.cs | 24 ++++++++++- .../Components/Timeline/TimelineDragBox.cs | 42 ++++++------------- 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 721f0c4e3b..a73ada76f5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -304,10 +304,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// public double VisibleRange => editorClock.TrackLength / Zoom; - public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) => - new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition)))); + public double TimeAtPosition(float x) + { + return x / Content.DrawWidth * editorClock.TrackLength; + } - private double getTimeFromPosition(Vector2 localPosition) => - (localPosition.X / Content.DrawWidth) * editorClock.TrackLength; + public float PositionAtTime(double time) + { + return (float)(time / editorClock.TrackLength * Content.DrawWidth); + } + + public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) + { + double time = TimeAtPosition(Content.ToLocalSpace(screenSpacePosition).X); + return new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(time)); + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index da80ed5ad6..05897e6d97 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -175,7 +175,29 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }; } - protected override DragBox CreateDragBox() => new TimelineDragBox(); + protected sealed override DragBox CreateDragBox() => new TimelineDragBox(); + + protected override void UpdateSelectionFromDragBox() + { + var dragBox = (TimelineDragBox)DragBox; + double minTime = dragBox.MinTime; + double maxTime = dragBox.MaxTime; + Console.WriteLine($"{minTime}, {maxTime}"); + + // TODO: performance + foreach (var hitObject in Beatmap.HitObjects) + { + bool shouldBeSelected = minTime <= hitObject.StartTime && hitObject.StartTime <= maxTime; + bool isSelected = SelectedItems.Contains(hitObject); + if (isSelected != shouldBeSelected) + { + if (!isSelected) + SelectedItems.Add(hitObject); + else + SelectedItems.Remove(hitObject); + } + } + } private void handleScrollViaDrag(DragEvent e) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs index 8b901c8958..4b16848c58 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs @@ -14,11 +14,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineDragBox : DragBox { - // the following values hold the start and end X positions of the drag box in the timeline's local space, - // but with zoom unapplied in order to be able to compensate for positional changes - // while the timeline is being zoomed in/out. - private float? selectionStart; - private float selectionEnd; + public double MinTime => Math.Min(startTime.Value, endTime); + + public double MaxTime => Math.Max(startTime.Value, endTime); + + private double? startTime; + + private double endTime; [Resolved] private Timeline timeline { get; set; } @@ -31,37 +33,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override void HandleDrag(MouseButtonEvent e) { - selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; + startTime ??= timeline.TimeAtPosition(e.MouseDownPosition.X); + endTime = timeline.TimeAtPosition(e.MousePosition.X); - // only calculate end when a transition is not in progress to avoid bouncing. - if (Precision.AlmostEquals(timeline.CurrentZoom, timeline.Zoom)) - selectionEnd = e.MousePosition.X / timeline.CurrentZoom; - - updateDragBoxPosition(); - } - - private void updateDragBoxPosition() - { - if (selectionStart == null) - return; - - float rescaledStart = selectionStart.Value * timeline.CurrentZoom; - float rescaledEnd = selectionEnd * timeline.CurrentZoom; - - Box.X = Math.Min(rescaledStart, rescaledEnd); - Box.Width = Math.Abs(rescaledStart - rescaledEnd); - - var boxScreenRect = Box.ScreenSpaceDrawQuad.AABBFloat; - - // we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment. - boxScreenRect.Y -= boxScreenRect.Height; - boxScreenRect.Height *= 2; + Box.X = timeline.PositionAtTime(MinTime); + Box.Width = timeline.PositionAtTime(MaxTime) - Box.X; } public override void Hide() { base.Hide(); - selectionStart = null; + startTime = null; } } } From 0613388aaa8bb5ae656d1237816e898c5f59b855 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 20:48:35 +0900 Subject: [PATCH 156/295] Make sure all selected items get deleted --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 2 +- osu.Game/Screens/Edit/EditorBeatmap.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 8419d3b380..269c19f846 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -305,7 +305,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected void DeleteSelected() { - DeleteItems(selectedBlueprints.Select(b => b.Item)); + DeleteItems(SelectedItems.ToArray()); } #endregion diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 16c0064e80..839535b99f 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -352,6 +352,8 @@ namespace osu.Game.Screens.Edit var updates = batchPendingUpdates.ToArray(); batchPendingUpdates.Clear(); + foreach (var h in deletes) SelectedHitObjects.Remove(h); + foreach (var h in deletes) HitObjectRemoved?.Invoke(h); foreach (var h in inserts) HitObjectAdded?.Invoke(h); foreach (var h in updates) HitObjectUpdated?.Invoke(h); From 00b3d97f69abaaed8a29cca1770aca91966e6fc6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 21:26:00 +0900 Subject: [PATCH 157/295] Improve timeline selection performance But selecting a large number of hit objects is still very slow because all DHOs must be added and also `AddBlueprintFor` has quadratic behaviors --- .../Compose/Components/BlueprintContainer.cs | 10 ++++++++-- .../Timeline/TimelineBlueprintContainer.cs | 18 ++++-------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 787959d214..da7a8e662b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -58,13 +58,19 @@ namespace osu.Game.Screens.Edit.Compose.Components { case NotifyCollectionChangedAction.Add: foreach (object o in args.NewItems) - SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Select(); + { + if (blueprintMap.TryGetValue((T)o, out var blueprint)) + blueprint.Select(); + } break; case NotifyCollectionChangedAction.Remove: foreach (object o in args.OldItems) - SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Deselect(); + { + if (blueprintMap.TryGetValue((T)o, out var blueprint)) + blueprint.Deselect(); + } break; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 05897e6d97..2d6dc797ca 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -182,21 +182,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var dragBox = (TimelineDragBox)DragBox; double minTime = dragBox.MinTime; double maxTime = dragBox.MaxTime; - Console.WriteLine($"{minTime}, {maxTime}"); - // TODO: performance - foreach (var hitObject in Beatmap.HitObjects) - { - bool shouldBeSelected = minTime <= hitObject.StartTime && hitObject.StartTime <= maxTime; - bool isSelected = SelectedItems.Contains(hitObject); - if (isSelected != shouldBeSelected) - { - if (!isSelected) - SelectedItems.Add(hitObject); - else - SelectedItems.Remove(hitObject); - } - } + SelectedItems.RemoveAll(hitObject => !shouldBeSelected(hitObject)); + SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).Where(hitObject => shouldBeSelected(hitObject))); + + bool shouldBeSelected(HitObject hitObject) => minTime <= hitObject.StartTime && hitObject.StartTime <= maxTime; } private void handleScrollViaDrag(DragEvent e) From 3108c42ecea1e08ac5a66e98ab7f798c42757fd6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 22:04:43 +0900 Subject: [PATCH 158/295] Fix inspect issues --- .../Timeline/TimelineBlueprintContainer.cs | 3 +-- .../Compose/Components/Timeline/TimelineDragBox.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 2d6dc797ca..04575e55b1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -184,7 +183,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline double maxTime = dragBox.MaxTime; SelectedItems.RemoveAll(hitObject => !shouldBeSelected(hitObject)); - SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).Where(hitObject => shouldBeSelected(hitObject))); + SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).Where(shouldBeSelected)); bool shouldBeSelected(HitObject hitObject) => minTime <= hitObject.StartTime && hitObject.StartTime <= maxTime; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs index 4b16848c58..65d9293b7e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs @@ -8,20 +8,17 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Utils; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineDragBox : DragBox { - public double MinTime => Math.Min(startTime.Value, endTime); + public double MinTime { get; private set; } - public double MaxTime => Math.Max(startTime.Value, endTime); + public double MaxTime { get; private set; } private double? startTime; - private double endTime; - [Resolved] private Timeline timeline { get; set; } @@ -34,7 +31,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override void HandleDrag(MouseButtonEvent e) { startTime ??= timeline.TimeAtPosition(e.MouseDownPosition.X); - endTime = timeline.TimeAtPosition(e.MousePosition.X); + double endTime = timeline.TimeAtPosition(e.MousePosition.X); + + MinTime = Math.Min(startTime.Value, endTime); + MaxTime = Math.Max(startTime.Value, endTime); Box.X = timeline.PositionAtTime(MinTime); Box.Width = timeline.PositionAtTime(MaxTime) - Box.X; From 6753f6b01ab353baaa6575400d074292e2831705 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 22:14:11 +0900 Subject: [PATCH 159/295] Move `AllowDeselectionDuringDrag` down Because it is now ignored in the timeline implementation anyways --- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 2 ++ .../Screens/Edit/Compose/Components/EditorBlueprintContainer.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 4c37d200bc..43ead88d54 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -83,6 +83,8 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + protected override bool AllowDeselectionDuringDrag => !EditorClock.IsRunning; + protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) { base.TransferBlueprintFor(hitObject, drawableObject); diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 6a4fe27f04..879ac58887 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -66,8 +66,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override IEnumerable> SortForMovement(IReadOnlyList> blueprints) => blueprints.OrderBy(b => b.Item.StartTime); - protected override bool AllowDeselectionDuringDrag => !EditorClock.IsRunning; - protected override bool ApplySnapResult(SelectionBlueprint[] blueprints, SnapResult result) { if (!base.ApplySnapResult(blueprints, result)) From b0213c29e98b3b59efdb16c3ff6457c592f5385b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 22:19:22 +0900 Subject: [PATCH 160/295] Use mid time instead of start time It is closer to the old blueprint-based behavior --- .../Components/Timeline/TimelineBlueprintContainer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 04575e55b1..08682ae05f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -185,7 +185,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline SelectedItems.RemoveAll(hitObject => !shouldBeSelected(hitObject)); SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).Where(shouldBeSelected)); - bool shouldBeSelected(HitObject hitObject) => minTime <= hitObject.StartTime && hitObject.StartTime <= maxTime; + bool shouldBeSelected(HitObject hitObject) + { + double midTime = (hitObject.StartTime + hitObject.GetEndTime()) / 2; + return minTime <= midTime && midTime <= maxTime; + } } private void handleScrollViaDrag(DragEvent e) From 2a7476cc4a820015898ac9104bacde3e0dc8e10d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 5 Oct 2022 23:29:45 +0900 Subject: [PATCH 161/295] Add test for timeline drag selection --- .../Editing/TestSceneTimelineSelection.cs | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs index 7e0981ce69..a4f4c375bf 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs @@ -29,16 +29,18 @@ namespace osu.Game.Tests.Visual.Editing private TimelineBlueprintContainer blueprintContainer => Editor.ChildrenOfType().First(); + private Vector2 getPosition(HitObject hitObject) => + blueprintContainer.SelectionBlueprints.First(s => s.Item == hitObject).ScreenSpaceDrawQuad.Centre; + + private Vector2 getMiddlePosition(HitObject hitObject1, HitObject hitObject2) => + (getPosition(hitObject1) + getPosition(hitObject2)) / 2; + private void moveMouseToObject(Func targetFunc) { AddStep("move mouse to object", () => { - var pos = blueprintContainer.SelectionBlueprints - .First(s => s.Item == targetFunc()) - .ChildrenOfType() - .First().ScreenSpaceDrawQuad.Centre; - - InputManager.MoveMouseTo(pos); + var hitObject = targetFunc(); + InputManager.MoveMouseTo(getPosition(hitObject)); }); } @@ -262,6 +264,55 @@ namespace osu.Game.Tests.Visual.Editing AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); } + [Test] + public void TestBasicDragSelection() + { + var addedObjects = new[] + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 500, Position = new Vector2(100) }, + new HitCircle { StartTime = 1000, Position = new Vector2(200) }, + new HitCircle { StartTime = 1500, Position = new Vector2(300) }, + }; + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); + + AddStep("move mouse", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[0], addedObjects[1]))); + AddStep("mouse down", () => InputManager.PressButton(MouseButton.Left)); + + AddStep("drag to select", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[2], addedObjects[3]))); + assertSelectionIs(new[] { addedObjects[1], addedObjects[2] }); + + AddStep("drag to deselect", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[1], addedObjects[2]))); + assertSelectionIs(new[] { addedObjects[1] }); + + AddStep("mouse up", () => InputManager.ReleaseButton(MouseButton.Left)); + assertSelectionIs(new[] { addedObjects[1] }); + } + + [Test] + public void TestFastDragSelection() + { + var addedObjects = new[] + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 500 }, + new HitCircle { StartTime = 20000, Position = new Vector2(100) }, + new HitCircle { StartTime = 31000, Position = new Vector2(200) }, + new HitCircle { StartTime = 60000, Position = new Vector2(300) }, + }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); + + AddStep("move mouse", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[0], addedObjects[1]))); + AddStep("mouse down", () => InputManager.PressButton(MouseButton.Left)); + AddStep("start drag", () => InputManager.MoveMouseTo(getPosition(addedObjects[1]))); + + AddStep("jump editor clock", () => EditorClock.Seek(30000)); + AddStep("jump editor clock", () => EditorClock.Seek(60000)); + AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left)); + assertSelectionIs(addedObjects.Skip(1)); + } + private void assertSelectionIs(IEnumerable hitObjects) => AddAssert("correct hitobjects selected", () => EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).SequenceEqual(hitObjects)); } From 2c0cd9ea526137ac675457936abe8224f3bcc8fc Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Wed, 5 Oct 2022 19:09:34 -0700 Subject: [PATCH 162/295] Add more smoke tests --- osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs | 68 +++++++++++++++---- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs index 82417d09a7..1cb64b71fc 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Framework.Input.States; +using osu.Framework.Logging; using osu.Framework.Testing.Input; using osu.Game.Rulesets.Osu.UI; using osuTK; @@ -17,22 +19,57 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestSmoking() { - AddStep("Create smoke", () => + addStep("Create short smoke", 2_000); + addStep("Create medium smoke", 5_000); + addStep("Create long smoke", 10_000); + } + + private void addStep(string stepName, double duration) + { + var smokeContainers = new List(); + + AddStep(stepName, () => { - SetContents(_ => new SmokingInputManager + smokeContainers.Clear(); + SetContents(_ => { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.95f), - Child = new TestSmokeContainer { RelativeSizeAxes = Axes.Both }, + smokeContainers.Add(new TestSmokeContainer + { + Duration = duration, + RelativeSizeAxes = Axes.Both + }); + + return new SmokingInputManager + { + Duration = duration, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.95f), + Child = smokeContainers[^1], + }; }); }); + + AddUntilStep("Until skinnable expires", () => + { + if (smokeContainers.Count == 0) + return false; + + Logger.Log("How many: " + smokeContainers.Count); + + foreach (var smokeContainer in smokeContainers) + { + if (smokeContainer.Children.Count != 0) + return false; + } + + return true; + }); } - private const double spin_duration = 5_000; - private const float spin_angle = 4 * MathF.PI; - private class SmokingInputManager : ManualInputManager { + public double Duration { get; init; } + private double? startTime; public SmokingInputManager() @@ -51,9 +88,11 @@ namespace osu.Game.Rulesets.Osu.Tests { base.Update(); + const float spin_angle = 4 * MathF.PI; + startTime ??= Time.Current; - float fraction = (float)((Time.Current - startTime) / spin_duration); + float fraction = (float)((Time.Current - startTime) / Duration); float angle = fraction * spin_angle; float radius = fraction * Math.Min(DrawSize.X, DrawSize.Y) / 2; @@ -65,24 +104,27 @@ namespace osu.Game.Rulesets.Osu.Tests private class TestSmokeContainer : SmokeContainer { - private double? startTime; + public double Duration { get; init; } + private bool isPressing; private bool isFinished; + private double? startTime; + protected override void Update() { base.Update(); - startTime ??= Time.Current; + startTime ??= Time.Current + 0.1; - if (!isPressing && !isFinished && Time.Current > startTime + 0.1) + if (!isPressing && !isFinished && Time.Current > startTime) { OnPressed(new KeyBindingPressEvent(new InputState(), OsuAction.Smoke)); isPressing = true; isFinished = false; } - if (isPressing && Time.Current > startTime + spin_duration) + if (isPressing && Time.Current > startTime + Duration) { OnReleased(new KeyBindingReleaseEvent(new InputState(), OsuAction.Smoke)); isPressing = false; From 49e023f8613d44ab03e40fda8236e4bbb0d5e774 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Wed, 5 Oct 2022 19:11:38 -0700 Subject: [PATCH 163/295] Rename `OsuSkinComponents.SmokeTrail` to `CursorSmoke` --- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 2 +- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 2 +- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index 3ee30ff7dc..4248cce55a 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu SliderBall, SliderBody, SpinnerBody, - SmokeTrail, + CursorSmoke, ApproachCircle, } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 4d12b85770..b778bc21d1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; - case OsuSkinComponents.SmokeTrail: + case OsuSkinComponents.CursorSmoke: if (GetTexture("cursor-smoke") != null) return new LegacySmokeSegment(); diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index 5999abc1bf..84596c6d72 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.UI { if (e.Action == OsuAction.Smoke) { - AddInternal(currentSegmentSkinnable = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SmokeTrail), _ => new DefaultSmokeSegment())); + AddInternal(currentSegmentSkinnable = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorSmoke), _ => new DefaultSmokeSegment())); // Add initial position immediately. addPosition(); From 9d54467145af496dab6d9f6a38517b4e1903ceaa Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Wed, 5 Oct 2022 19:13:06 -0700 Subject: [PATCH 164/295] Make smoke skinnable lifetime more robust --- .../Skinning/SmokeSegment.cs | 2 +- osu.Game.Rulesets.Osu/UI/SmokeContainer.cs | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 99196f6967..6c998e244c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Skinning RelativeSizeAxes = Axes.Both; - smokeStartTime = Time.Current; + LifetimeStart = smokeStartTime = Time.Current; totalDistance = pointInterval; } diff --git a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs index 84596c6d72..beba834e88 100644 --- a/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/SmokeContainer.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -17,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.UI /// public class SmokeContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler { - private SkinnableDrawable? currentSegmentSkinnable; + private SmokeSkinnableDrawable? currentSegmentSkinnable; private Vector2 lastMousePosition; @@ -27,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.UI { if (e.Action == OsuAction.Smoke) { - AddInternal(currentSegmentSkinnable = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorSmoke), _ => new DefaultSmokeSegment())); + AddInternal(currentSegmentSkinnable = new SmokeSkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorSmoke), _ => new DefaultSmokeSegment())); // Add initial position immediately. addPosition(); @@ -44,8 +46,6 @@ namespace osu.Game.Rulesets.Osu.UI if (currentSegmentSkinnable?.Drawable is SmokeSegment segment) { segment.FinishDrawing(Time.Current); - - currentSegmentSkinnable.LifetimeEnd = segment.LifetimeEnd; currentSegmentSkinnable = null; } } @@ -60,5 +60,18 @@ namespace osu.Game.Rulesets.Osu.UI } private void addPosition() => (currentSegmentSkinnable?.Drawable as SmokeSegment)?.AddPosition(lastMousePosition, Time.Current); + + private class SmokeSkinnableDrawable : SkinnableDrawable + { + public override bool RemoveWhenNotAlive => true; + + public override double LifetimeStart => Drawable.LifetimeStart; + public override double LifetimeEnd => Drawable.LifetimeEnd; + + public SmokeSkinnableDrawable(ISkinComponent component, Func? defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling) + : base(component, defaultImplementation, confineMode) + { + } + } } } From 0d448e6cc898e181ab65f31280e3be15ee761f42 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 Oct 2022 13:50:56 +0900 Subject: [PATCH 165/295] Fix items without blueprints are not deselected --- .../Compose/Components/BlueprintContainer.cs | 18 +++++++----------- .../Components/EditorBlueprintContainer.cs | 4 ++-- .../Skinning/Editor/SkinBlueprintContainer.cs | 6 ++++++ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index da7a8e662b..8aecc75824 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Edit.Compose.Components }; SelectionHandler = CreateSelectionHandler(); - SelectionHandler.DeselectAll = deselectAll; + SelectionHandler.DeselectAll = DeselectAll; SelectionHandler.SelectedItems.BindTo(SelectedItems); AddRangeInternal(new[] @@ -145,7 +145,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (endClickSelection(e) || ClickedBlueprint != null) return true; - deselectAll(); + DeselectAll(); return true; } @@ -234,7 +234,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (!SelectionHandler.SelectedBlueprints.Any()) return false; - deselectAll(); + DeselectAll(); return true; } @@ -399,18 +399,14 @@ namespace osu.Game.Screens.Edit.Compose.Components } /// - /// Selects all s. + /// Select all currently-present items. /// - protected virtual void SelectAll() - { - // Scheduled to allow the change in lifetime to take place. - Schedule(() => SelectionBlueprints.ToList().ForEach(m => m.Select())); - } + protected abstract void SelectAll(); /// - /// Deselects all selected s. + /// Deselect all selected items. /// - private void deselectAll() => SelectionHandler.SelectedBlueprints.ToList().ForEach(m => m.Deselect()); + protected void DeselectAll() => SelectedItems.Clear(); protected virtual void OnBlueprintSelected(SelectionBlueprint blueprint) { diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 879ac58887..6682748253 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -131,8 +131,8 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void SelectAll() { Composer.Playfield.KeepAllAlive(); - - base.SelectAll(); + SelectedItems.Clear(); + SelectedItems.AddRange(Beatmap.HitObjects); } protected override void OnBlueprintSelected(SelectionBlueprint blueprint) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index 5a1ef34151..97522ddff8 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -117,6 +117,12 @@ namespace osu.Game.Skinning.Editor return false; } + protected override void SelectAll() + { + SelectedItems.Clear(); + SelectedItems.AddRange(targetComponents.SelectMany(list => list)); + } + /// /// Move the current selection spatially by the specified delta, in screen coordinates (ie. the same coordinates as the blueprints). /// From 29cc55463268c40b688d1125ea7b69886c046135 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 Oct 2022 13:59:54 +0900 Subject: [PATCH 166/295] Ensure blueprint is added for selected hit object --- .../Components/Timeline/TimelineBlueprintContainer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 08682ae05f..31990bfd35 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -183,7 +183,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline double maxTime = dragBox.MaxTime; SelectedItems.RemoveAll(hitObject => !shouldBeSelected(hitObject)); - SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).Where(shouldBeSelected)); + + foreach (var hitObject in Beatmap.HitObjects.Except(SelectedItems).Where(shouldBeSelected)) + { + Composer.Playfield.SetKeepAlive(hitObject, true); + SelectedItems.Add(hitObject); + } bool shouldBeSelected(HitObject hitObject) { From 0ade0492526b184b14f64e59a66e88a71fc1e5d3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 Oct 2022 14:02:49 +0900 Subject: [PATCH 167/295] Add test for selected hit object blueprint --- osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs index a4f4c375bf..54ad4e25e4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs @@ -311,6 +311,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("jump editor clock", () => EditorClock.Seek(60000)); AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left)); assertSelectionIs(addedObjects.Skip(1)); + AddAssert("all blueprints are present", () => blueprintContainer.SelectionBlueprints.Count == EditorBeatmap.SelectedHitObjects.Count); } private void assertSelectionIs(IEnumerable hitObjects) From 0f6a6287f246f974a9e21304846c6b0083d8d343 Mon Sep 17 00:00:00 2001 From: NullifiedJosh <86538544+NullifiedJosh@users.noreply.github.com> Date: Thu, 6 Oct 2022 18:17:33 +0800 Subject: [PATCH 168/295] Fix bugs and add test --- .../TestSceneDrawableCatchRuleset.cs | 59 +++++++++++++++++++ .../UI/DrawableCatchRuleset.cs | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs new file mode 100644 index 0000000000..b826a064e6 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Logging; +using osu.Framework.Testing; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class TestSceneDrawableCatchRulesetWithRelax : OsuTestScene + { + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create drawable ruleset with relax mod", () => + { + Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List() { + new CatchModRelax() + }); + }); + AddUntilStep("wait for load", () => Child.IsLoaded); + } + + [Test] + public void TestBasic() + { + AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any() == false); + } + } + + [TestFixture] + public class TestSceneDrawableCatchRulesetWithoutRelax : OsuTestScene + { + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create drawable ruleset without relax mod", () => + { + Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List()); + }); + AddUntilStep("wait for load", () => Child.IsLoaded); + Logger.Log("Ready"); + } + + [Test] + public void TestBasic() + { + AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any()); + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 07968e4bee..dfa6126d3f 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.UI : base(ruleset, beatmap, mods) { // Check if mods have RelaxMod instance - if (mods.OfType().Any()) + if (mods != null && mods.OfType().Any()) showMobileMapper = false; Direction.Value = ScrollingDirection.Down; From 6543171169b37a79b3b6ddb192dcd15b4332f4a5 Mon Sep 17 00:00:00 2001 From: NullifiedJosh <86538544+NullifiedJosh@users.noreply.github.com> Date: Thu, 6 Oct 2022 18:30:49 +0800 Subject: [PATCH 169/295] Fix formating. --- .../TestSceneDrawableCatchRuleset.cs | 38 +++++-------------- .../UI/DrawableCatchRuleset.cs | 2 +- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs index b826a064e6..8b794fd919 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Logging; using osu.Framework.Testing; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Mods; @@ -15,45 +14,28 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests { [TestFixture] - public class TestSceneDrawableCatchRulesetWithRelax : OsuTestScene + public class TestSceneDrawableCatchRuleset : OsuTestScene { - [SetUpSteps] - public void SetUpSteps() - { - AddStep("create drawable ruleset with relax mod", () => - { - Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List() { - new CatchModRelax() - }); - }); - AddUntilStep("wait for load", () => Child.IsLoaded); - } - [Test] - public void TestBasic() - { - AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any() == false); - } - } - - [TestFixture] - public class TestSceneDrawableCatchRulesetWithoutRelax : OsuTestScene - { - [SetUpSteps] - public void SetUpSteps() + public void TestWithoutRelax() { AddStep("create drawable ruleset without relax mod", () => { Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List()); }); AddUntilStep("wait for load", () => Child.IsLoaded); - Logger.Log("Ready"); + AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any()); } [Test] - public void TestBasic() + public void TestWithRelax() { - AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any()); + AddStep("create drawable ruleset with relax mod", () => + { + Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List { new CatchModRelax() }); + }); + AddUntilStep("wait for load", () => Child.IsLoaded); + AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any() == false); } } } diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index dfa6126d3f..ce1fa963e7 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - private bool showMobileMapper = true; + private readonly bool showMobileMapper = true; public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) From ea4dbc8c0f3ef669d86439a9b1a368c213d39a26 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 Oct 2022 19:46:10 +0900 Subject: [PATCH 170/295] Fix mania hold note head disappears --- .../Objects/Drawables/DrawableHoldNoteHead.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs index 66cc93b033..ac646ea427 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs @@ -36,6 +36,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // the hold note head should never change its visual state on its own due to the "freezing" mechanic // (when hit, it remains visible in place at the judgement line; when dropped, it will scroll past the line). // it will be hidden along with its parenting hold note when required. + + // Set `LifetimeEnd` explicitly to a non-`double.MaxValue` because otherwise this DHO is automatically expired. + LifetimeEnd = double.PositiveInfinity; } public override bool OnPressed(KeyBindingPressEvent e) => false; // Handled by the hold note From 994db55b6d542c187d96d6905552f79d2a352d6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 02:19:43 +0900 Subject: [PATCH 171/295] Simplify check conditionals --- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index ce1fa963e7..27f7886d79 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -27,15 +27,9 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - private readonly bool showMobileMapper = true; - public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { - // Check if mods have RelaxMod instance - if (mods != null && mods.OfType().Any()) - showMobileMapper = false; - Direction.Value = ScrollingDirection.Down; TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450); } @@ -43,7 +37,8 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - if (showMobileMapper) + // With relax mod, input maps directly to x position and left/right buttons are not used. + if (!Mods.Any(m => m is ModRelax)) KeyBindingInputManager.Add(new CatchTouchInputMapper()); } From f3262103c44ad9d430e8e3883ccb490c0916e23e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 02:21:31 +0900 Subject: [PATCH 172/295] Move test into existing catch touch test scene --- .../TestSceneCatchTouchInput.cs | 35 +++++++++++++--- .../TestSceneDrawableCatchRuleset.cs | 41 ------------------- 2 files changed, 29 insertions(+), 47 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchTouchInput.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchTouchInput.cs index cbf6e8f202..cf6a8169c4 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchTouchInput.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchTouchInput.cs @@ -1,10 +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.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Mods; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -12,11 +17,11 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneCatchTouchInput : OsuTestScene { - private CatchTouchInputMapper catchTouchInputMapper = null!; - - [SetUpSteps] - public void SetUpSteps() + [Test] + public void TestBasic() { + CatchTouchInputMapper catchTouchInputMapper = null!; + AddStep("create input overlay", () => { Child = new CatchInputManager(new CatchRuleset().RulesetInfo) @@ -32,12 +37,30 @@ namespace osu.Game.Rulesets.Catch.Tests } }; }); + + AddStep("show overlay", () => catchTouchInputMapper.Show()); } [Test] - public void TestBasic() + public void TestWithoutRelax() { - AddStep("show overlay", () => catchTouchInputMapper.Show()); + AddStep("create drawable ruleset without relax mod", () => + { + Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List()); + }); + AddUntilStep("wait for load", () => Child.IsLoaded); + AddAssert("check touch input is shown", () => this.ChildrenOfType().Any()); + } + + [Test] + public void TestWithRelax() + { + AddStep("create drawable ruleset with relax mod", () => + { + Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List { new CatchModRelax() }); + }); + AddUntilStep("wait for load", () => Child.IsLoaded); + AddAssert("check touch input is not shown", () => !this.ChildrenOfType().Any()); } } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs deleted file mode 100644 index 8b794fd919..0000000000 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableCatchRuleset.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Testing; -using osu.Game.Rulesets.Catch.Beatmaps; -using osu.Game.Rulesets.Catch.Mods; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - public class TestSceneDrawableCatchRuleset : OsuTestScene - { - [Test] - public void TestWithoutRelax() - { - AddStep("create drawable ruleset without relax mod", () => - { - Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List()); - }); - AddUntilStep("wait for load", () => Child.IsLoaded); - AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any()); - } - - [Test] - public void TestWithRelax() - { - AddStep("create drawable ruleset with relax mod", () => - { - Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List { new CatchModRelax() }); - }); - AddUntilStep("wait for load", () => Child.IsLoaded); - AddAssert("check if touch catcher is showing", () => this.ChildrenOfType().Any() == false); - } - } -} From 6164e0896a8ec69e87329f790e22624bba85225d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 7 Oct 2022 10:46:07 +0900 Subject: [PATCH 173/295] Don't reselect already selected items in SelectAll --- .../Edit/Compose/Components/EditorBlueprintContainer.cs | 3 +-- osu.Game/Skinning/Editor/SkinBlueprintContainer.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 6682748253..6adaeb1a83 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -131,8 +131,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void SelectAll() { Composer.Playfield.KeepAllAlive(); - SelectedItems.Clear(); - SelectedItems.AddRange(Beatmap.HitObjects); + SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).ToArray()); } protected override void OnBlueprintSelected(SelectionBlueprint blueprint) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index 97522ddff8..2937b62eec 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -119,8 +119,7 @@ namespace osu.Game.Skinning.Editor protected override void SelectAll() { - SelectedItems.Clear(); - SelectedItems.AddRange(targetComponents.SelectMany(list => list)); + SelectedItems.AddRange(targetComponents.SelectMany(list => list).Except(SelectedItems).ToArray()); } /// From b27e70ca479e622b12046722d8832b8d0ee81b63 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 6 Oct 2022 21:11:00 -0700 Subject: [PATCH 174/295] Fix language settings dropdown not updating when changing language in first run setup --- .../Settings/Sections/General/LanguageSettings.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index 63f0dec953..0f77e6609b 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -44,9 +44,12 @@ namespace osu.Game.Overlays.Settings.Sections.General }, }; - if (!LanguageExtensions.TryParseCultureCode(frameworkLocale.Value, out var locale)) - locale = Language.en; - languageSelection.Current.Value = locale; + frameworkLocale.BindValueChanged(locale => + { + if (!LanguageExtensions.TryParseCultureCode(locale.NewValue, out var language)) + language = Language.en; + languageSelection.Current.Value = language; + }, true); languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode()); } From 7385ef3e1b033c37bc4bec0737ac446efe4eaa60 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 7 Oct 2022 14:26:19 +0900 Subject: [PATCH 175/295] Extract combo scale to virtual function --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index a594363d4c..69937a0fba 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -149,16 +149,21 @@ namespace osu.Game.Rulesets.Mods float size = defaultFlashlightSize * sizeMultiplier; if (comboBasedSize) - { - if (combo >= 200) - size *= 0.625f; - else if (combo >= 100) - size *= 0.8125f; - } + size *= GetComboScaleFor(combo); return size; } + protected virtual float GetComboScaleFor(int combo) + { + if (combo >= 200) + return 0.625f; + if (combo >= 100) + return 0.8125f; + + return 1.0f; + } + private Vector2 flashlightPosition; protected Vector2 FlashlightPosition From c6b5fdc7d0cdab0d41cc9e41cf9e509433633427 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 7 Oct 2022 14:34:48 +0900 Subject: [PATCH 176/295] Adjust catch flashlight to closely match classic --- osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index 1adc969f8f..ff957b9b73 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods public override BindableBool ComboBasedSize { get; } = new BindableBool(true); - public override float DefaultFlashlightSize => 350; + public override float DefaultFlashlightSize => 325; protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield); @@ -44,7 +44,19 @@ namespace osu.Game.Rulesets.Catch.Mods : base(modFlashlight) { this.playfield = playfield; + FlashlightSize = new Vector2(0, GetSizeFor(0)); + FlashlightSmoothness = 1.4f; + } + + protected override float GetComboScaleFor(int combo) + { + if (combo >= 200) + return 0.770f; + if (combo >= 100) + return 0.885f; + + return 1.0f; } protected override void Update() From 5f3b58b7e009a0ed5c7d1028a01d89c7e754a2f4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 7 Oct 2022 14:44:45 +0900 Subject: [PATCH 177/295] Adjust taiko flashlight to closely match classic --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 1caacdd1d7..fca69e86cc 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public override BindableBool ComboBasedSize { get; } = new BindableBool(true); - public override float DefaultFlashlightSize => 250; + public override float DefaultFlashlightSize => 200; protected override Flashlight CreateFlashlight() => new TaikoFlashlight(this, playfield); @@ -46,7 +46,9 @@ namespace osu.Game.Rulesets.Taiko.Mods : base(modFlashlight) { this.taikoPlayfield = taikoPlayfield; + FlashlightSize = getSizeFor(0); + FlashlightSmoothness = 1.4f; AddLayout(flashlightProperties); } From df3ad618e15782e28b08fa4271aa1e79560de6b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 17:49:00 +0900 Subject: [PATCH 178/295] Move `ColumnType` to constructor --- .../Editor/ManiaPlacementBlueprintTestScene.cs | 3 ++- .../Skinning/ColumnTestContainer.cs | 3 +-- osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs | 3 ++- .../TestSceneDrawableManiaHitObject.cs | 3 ++- osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs | 2 -- osu.Game.Rulesets.Mania/UI/Column.cs | 13 ++++++------- osu.Game.Rulesets.Mania/UI/Stage.cs | 5 ++--- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs index 6e6e83f9cf..d6dd2664bc 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { scrollingInfo = ((ScrollingTestContainer)HitObjectContainer).ScrollingInfo; - Add(column = new Column(0) + Add(column = new Column(0, ColumnType.Even) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs index 1a3513d46c..33166c5aeb 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs @@ -28,11 +28,10 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { InternalChildren = new[] { - this.column = new Column(column) + this.column = new Column(column, column % 2 == 0 ? ColumnType.Even : ColumnType.Odd) { Action = { Value = action }, AccentColour = Color4.Orange, - ColumnType = column % 2 == 0 ? ColumnType.Even : ColumnType.Odd, Alpha = showColumn ? 1 : 0 }, content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs index 2922d18713..ab2f63339f 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; @@ -84,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.Tests private Drawable createColumn(ScrollingDirection direction, ManiaAction action, int index) { - var column = new Column(index) + var column = new Column(index, ColumnType.Even) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs index 223f8dae44..b9b28eb19f 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs @@ -9,6 +9,7 @@ using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; @@ -35,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Tests RelativeSizeAxes = Axes.Y, TimeRange = 2000, Clock = new FramedClock(clock), - Child = column = new Column(0) + Child = column = new Column(0, ColumnType.Even) { Action = { Value = ManiaAction.Key1 }, Height = 0.85f, diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs b/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs index 0114987e3c..8f904530bc 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - namespace osu.Game.Rulesets.Mania.Beatmaps { public enum ColumnType diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index deb1b155b5..361210ff1d 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -46,9 +46,14 @@ namespace osu.Game.Rulesets.Mania.UI private readonly GameplaySampleTriggerSource sampleTriggerSource; - public Column(int index) + public readonly ColumnType ColumnType; + + public Color4 AccentColour { get; set; } + + public Column(int index, ColumnType columnType) { Index = index; + ColumnType = columnType; RelativeSizeAxes = Axes.Y; Width = COLUMN_WIDTH; @@ -92,12 +97,6 @@ namespace osu.Game.Rulesets.Mania.UI NewResult += OnNewResult; } - public ColumnType ColumnType { get; set; } - - public bool IsSpecial => ColumnType == ColumnType.Special; - - public Color4 AccentColour { get; set; } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index c578bbb703..29dacc5094 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -120,13 +120,12 @@ namespace osu.Game.Rulesets.Mania.UI { var columnType = definition.GetTypeOfColumn(i); - var column = new Column(firstColumnIndex + i) + var column = new Column(firstColumnIndex + i, columnType) { RelativeSizeAxes = Axes.Both, - Width = 1, - ColumnType = columnType, AccentColour = columnColours[columnType], Action = { Value = columnType == ColumnType.Special ? specialColumnStartAction++ : normalColumnStartAction++ } + Width = 1, }; topLevelContainer.Add(column.TopLevelContainer.CreateProxy()); From 7796a4c1097c0de6e780f282db5883a49357aa18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 18:24:49 +0900 Subject: [PATCH 179/295] Cache `StageDefinition` for consumption (and remove `ColumnType`) --- .../ManiaPlacementBlueprintTestScene.cs | 3 +- ...nTypeTest.cs => ManiaSpecialColumnTest.cs} | 34 +++++++++---------- .../Skinning/ColumnTestContainer.cs | 5 ++- .../TestSceneColumn.cs | 3 +- .../TestSceneDrawableManiaHitObject.cs | 3 +- .../Beatmaps/ColumnType.cs | 12 ------- .../Beatmaps/StageDefinition.cs | 19 ++--------- osu.Game.Rulesets.Mania/ManiaSkinComponent.cs | 4 +-- .../Legacy/LegacyManiaColumnElement.cs | 21 +++++------- .../Legacy/ManiaLegacySkinTransformer.cs | 2 +- osu.Game.Rulesets.Mania/UI/Column.cs | 13 +++---- osu.Game.Rulesets.Mania/UI/Stage.cs | 27 +++++++++------ 12 files changed, 61 insertions(+), 85 deletions(-) rename osu.Game.Rulesets.Mania.Tests/{ManiaColumnTypeTest.cs => ManiaSpecialColumnTest.cs} (53%) delete mode 100644 osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs index d6dd2664bc..976efc51b2 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -35,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { scrollingInfo = ((ScrollingTestContainer)HitObjectContainer).ScrollingInfo; - Add(column = new Column(0, ColumnType.Even) + Add(column = new Column(0, false) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs similarity index 53% rename from osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs rename to osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs index e53deb5269..c83e8a51ed 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs @@ -10,43 +10,43 @@ using NUnit.Framework; namespace osu.Game.Rulesets.Mania.Tests { [TestFixture] - public class ManiaColumnTypeTest + public class ManiaSpecialColumnTest { [TestCase(new[] { - ColumnType.Special + true }, 1)] [TestCase(new[] { - ColumnType.Odd, - ColumnType.Even, - ColumnType.Even, - ColumnType.Odd + false, + false, + false, + false }, 4)] [TestCase(new[] { - ColumnType.Odd, - ColumnType.Even, - ColumnType.Odd, - ColumnType.Special, - ColumnType.Odd, - ColumnType.Even, - ColumnType.Odd + false, + false, + false, + true, + false, + false, + false }, 7)] - public void Test(IEnumerable expected, int columns) + public void Test(IEnumerable special, int columns) { var definition = new StageDefinition { Columns = columns }; var results = getResults(definition); - Assert.AreEqual(expected, results); + Assert.AreEqual(special, results); } - private IEnumerable getResults(StageDefinition definition) + private IEnumerable getResults(StageDefinition definition) { for (int i = 0; i < definition.Columns; i++) - yield return definition.GetTypeOfColumn(i); + yield return definition.IsSpecialColumn(i); } } } diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs index 33166c5aeb..b8d1313af5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs @@ -24,11 +24,14 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [Cached] private readonly Column column; + [Cached] + private readonly StageDefinition stageDefinition = new StageDefinition { Columns = 1 }; + public ColumnTestContainer(int column, ManiaAction action, bool showColumn = false) { InternalChildren = new[] { - this.column = new Column(column, column % 2 == 0 ? ColumnType.Even : ColumnType.Odd) + this.column = new Column(column, false) { Action = { Value = action }, AccentColour = Color4.Orange, diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs index ab2f63339f..8fff87ae78 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs @@ -11,7 +11,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; @@ -85,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Tests private Drawable createColumn(ScrollingDirection direction, ManiaAction action, int index) { - var column = new Column(index, ColumnType.Even) + var column = new Column(index, false) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs index b9b28eb19f..bd34f1ef11 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs @@ -9,7 +9,6 @@ using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; @@ -36,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests RelativeSizeAxes = Axes.Y, TimeRange = 2000, Clock = new FramedClock(clock), - Child = column = new Column(0, ColumnType.Even) + Child = column = new Column(0, false) { Action = { Value = ManiaAction.Key1 }, Height = 0.85f, diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs b/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs deleted file mode 100644 index 8f904530bc..0000000000 --- a/osu.Game.Rulesets.Mania/Beatmaps/ColumnType.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Mania.Beatmaps -{ - public enum ColumnType - { - Even, - Odd, - Special - } -} diff --git a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs index 54e2d4686f..08ee97b32b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using osu.Game.Rulesets.Mania.UI; namespace osu.Game.Rulesets.Mania.Beatmaps @@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// /// Defines properties for each stage in a . /// - public struct StageDefinition + public class StageDefinition { /// /// The number of s which this stage contains. @@ -23,20 +22,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// /// The 0-based column index. /// Whether the column is a special column. - public readonly bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2; - - /// - /// Get the type of column given a column index. - /// - /// The 0-based column index. - /// The type of the column. - public readonly ColumnType GetTypeOfColumn(int column) - { - if (IsSpecialColumn(column)) - return ColumnType.Special; - - int distanceToEdge = Math.Min(column, (Columns - 1) - column); - return distanceToEdge % 2 == 0 ? ColumnType.Odd : ColumnType.Even; - } + public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2; } } diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs index 21b362df00..6d0eb15b64 100644 --- a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs +++ b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs @@ -15,14 +15,14 @@ namespace osu.Game.Rulesets.Mania /// The intended for this component. /// May be null if the component is not a direct member of a . /// - public readonly StageDefinition? StageDefinition; + public readonly StageDefinition StageDefinition; /// /// Creates a new . /// /// The component. /// The intended for this component. May be null if the component is not a direct member of a . - public ManiaSkinComponent(ManiaSkinComponents component, StageDefinition? stageDefinition = null) + public ManiaSkinComponent(ManiaSkinComponents component, StageDefinition stageDefinition = null) : base(component) { StageDefinition = stageDefinition; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs index ab953ccfb9..e227c80845 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; @@ -20,6 +21,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy [Resolved] protected Column Column { get; private set; } + [Resolved] + private StageDefinition stage { get; set; } + /// /// The column type identifier to use for texture lookups, in the case of no user-provided configuration. /// @@ -28,19 +32,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy [BackgroundDependencyLoader] private void load() { - switch (Column.ColumnType) + if (Column.IsSpecial) + FallbackColumnIndex = "S"; + else { - case ColumnType.Special: - FallbackColumnIndex = "S"; - break; - - case ColumnType.Odd: - FallbackColumnIndex = "1"; - break; - - case ColumnType.Even: - FallbackColumnIndex = "2"; - break; + int distanceToEdge = Math.Min(Column.Index, (stage.Columns - 1) - Column.Index); + FallbackColumnIndex = distanceToEdge % 2 == 0 ? "1" : "2"; } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index dd5baa8150..a1b196e53e 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy case ManiaSkinComponents.StageBackground: Debug.Assert(maniaComponent.StageDefinition != null); - return new LegacyStageBackground(maniaComponent.StageDefinition.Value); + return new LegacyStageBackground(maniaComponent.StageDefinition); case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 361210ff1d..57ada84767 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -6,7 +6,6 @@ using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -18,7 +17,6 @@ using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -26,7 +24,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.UI { [Cached] - public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour + public class Column : ScrollingPlayfield, IKeyBindingHandler { public const float COLUMN_WIDTH = 80; public const float SPECIAL_COLUMN_WIDTH = 70; @@ -46,14 +44,17 @@ namespace osu.Game.Rulesets.Mania.UI private readonly GameplaySampleTriggerSource sampleTriggerSource; - public readonly ColumnType ColumnType; + /// + /// Whether this is a special (ie. scratch) column. + /// + public readonly bool IsSpecial; public Color4 AccentColour { get; set; } - public Column(int index, ColumnType columnType) + public Column(int index, bool isSpecial) { Index = index; - ColumnType = columnType; + IsSpecial = isSpecial; RelativeSizeAxes = Axes.Y; Width = COLUMN_WIDTH; diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index 29dacc5094..9e51ed84db 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; @@ -19,7 +20,6 @@ using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.UI { @@ -28,6 +28,9 @@ namespace osu.Game.Rulesets.Mania.UI /// public class Stage : ScrollingPlayfield { + [Cached] + public readonly StageDefinition Definition; + public const float COLUMN_SPACING = 1; public const float HIT_TARGET_POSITION = 110; @@ -40,12 +43,12 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Drawable barLineContainer; - private readonly Dictionary columnColours = new Dictionary - { - { ColumnType.Even, new Color4(6, 84, 0, 255) }, - { ColumnType.Odd, new Color4(94, 0, 57, 255) }, - { ColumnType.Special, new Color4(0, 48, 63, 255) } - }; + // private readonly Dictionary columnColours = new Dictionary + // { + // { ColumnType.Even, new Color4(6, 84, 0, 255) }, + // { ColumnType.Odd, new Color4(94, 0, 57, 255) }, + // { ColumnType.Special, new Color4(0, 48, 63, 255) } + // }; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Columns.Any(c => c.ReceivePositionalInputAt(screenSpacePos)); @@ -54,6 +57,7 @@ namespace osu.Game.Rulesets.Mania.UI public Stage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction) { this.firstColumnIndex = firstColumnIndex; + Definition = definition; Name = "Stage"; @@ -118,14 +122,15 @@ namespace osu.Game.Rulesets.Mania.UI for (int i = 0; i < definition.Columns; i++) { - var columnType = definition.GetTypeOfColumn(i); + bool isSpecial = definition.IsSpecialColumn(i); - var column = new Column(firstColumnIndex + i, columnType) + var column = new Column(firstColumnIndex + i, isSpecial) { RelativeSizeAxes = Axes.Both, - AccentColour = columnColours[columnType], - Action = { Value = columnType == ColumnType.Special ? specialColumnStartAction++ : normalColumnStartAction++ } Width = 1, + // TODO: reimplement this somewhere. + //AccentColour = columnColours[isSpecial], + Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ } }; topLevelContainer.Add(column.TopLevelContainer.CreateProxy()); From 46c3cfe54d6a772b82c0a76f969627b3c408f30b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 18:26:27 +0900 Subject: [PATCH 180/295] Remove `StageDefinition` flow in `ManiaSkinComponent` --- .../Skinning/TestSceneStageBackground.cs | 3 +-- .../Skinning/TestSceneStageForeground.cs | 3 +-- osu.Game.Rulesets.Mania/ManiaSkinComponent.cs | 12 +----------- .../Skinning/Legacy/LegacyStageBackground.cs | 7 ++----- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 4 +--- osu.Game.Rulesets.Mania/UI/Stage.cs | 4 ++-- 6 files changed, 8 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs index 687b3a747d..0744d7e2e7 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Skinning; @@ -16,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }), + SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground()) { Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs index 6cbc172755..979c90c802 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.Tests.Skinning @@ -15,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null) + SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs index 6d0eb15b64..f05edb4677 100644 --- a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs +++ b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs @@ -3,29 +3,19 @@ #nullable disable -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.UI; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania { public class ManiaSkinComponent : GameplaySkinComponent { - /// - /// The intended for this component. - /// May be null if the component is not a direct member of a . - /// - public readonly StageDefinition StageDefinition; - /// /// Creates a new . /// /// The component. - /// The intended for this component. May be null if the component is not a direct member of a . - public ManiaSkinComponent(ManiaSkinComponents component, StageDefinition stageDefinition = null) + public ManiaSkinComponent(ManiaSkinComponents component) : base(component) { - StageDefinition = stageDefinition; } protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs index 740ccbfe27..d039551cd7 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs @@ -18,20 +18,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { public class LegacyStageBackground : CompositeDrawable { - private readonly StageDefinition stageDefinition; - private Drawable leftSprite; private Drawable rightSprite; private ColumnFlow columnBackgrounds; - public LegacyStageBackground(StageDefinition stageDefinition) + public LegacyStageBackground() { - this.stageDefinition = stageDefinition; RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, StageDefinition stageDefinition) { string leftImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftStageImage)?.Value ?? "mania-stage-left"; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index a1b196e53e..5ecdf988f9 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -113,8 +112,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy return new LegacyHitExplosion(); case ManiaSkinComponents.StageBackground: - Debug.Assert(maniaComponent.StageDefinition != null); - return new LegacyStageBackground(maniaComponent.StageDefinition); + return new LegacyStageBackground(); case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index 9e51ed84db..a19d423e69 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.UI AutoSizeAxes = Axes.X, Children = new Drawable[] { - new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: definition), _ => new DefaultStageBackground()) + new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground()) { RelativeSizeAxes = Axes.Both }, @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Y, } }, - new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: definition), _ => null) + new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null) { RelativeSizeAxes = Axes.Both }, From 9c979044dcd93f53cb86be594b8b6da3f382761c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 19:27:16 +0900 Subject: [PATCH 181/295] Move `AccentColour` assignment to inside `Column` --- osu.Game.Rulesets.Mania/UI/Column.cs | 42 ++++++++++++++++++---------- osu.Game.Rulesets.Mania/UI/Stage.cs | 2 -- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 57ada84767..c0739f5ec3 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -3,23 +3,24 @@ #nullable disable -using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI.Components; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.UI; +using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.UI { @@ -37,12 +38,12 @@ namespace osu.Game.Rulesets.Mania.UI public readonly Bindable Action = new Bindable(); public readonly ColumnHitObjectArea HitObjectArea; - internal readonly Container TopLevelContainer; - private readonly DrawablePool hitExplosionPool; + internal readonly Container TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }; + private DrawablePool hitExplosionPool; private readonly OrderedHitPolicy hitPolicy; public Container UnderlayElements => HitObjectArea.UnderlayElements; - private readonly GameplaySampleTriggerSource sampleTriggerSource; + private GameplaySampleTriggerSource sampleTriggerSource; /// /// Whether this is a special (ie. scratch) column. @@ -59,6 +60,21 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Y; Width = COLUMN_WIDTH; + hitPolicy = new OrderedHitPolicy(HitObjectContainer); + HitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both }; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + // TODO: reimplement this somewhere. + AccentColour = skin.GetConfig() + var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; + + //AccentColour = columnColours[isSpecial], + foreach (var obj in HitObjectContainer.Objects) + obj.AccentColour.Value = AccentColour; + Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both @@ -70,18 +86,16 @@ namespace osu.Game.Rulesets.Mania.UI sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer), // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements background.CreateProxy(), - HitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both }, + HitObjectArea, new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) { RelativeSizeAxes = Axes.Both }, background, - TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }, + TopLevelContainer, new ColumnTouchInputArea(this) }; - hitPolicy = new OrderedHitPolicy(HitObjectContainer); - TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); RegisterPool(10, 50); diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index a19d423e69..52c8829df3 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -128,8 +128,6 @@ namespace osu.Game.Rulesets.Mania.UI { RelativeSizeAxes = Axes.Both, Width = 1, - // TODO: reimplement this somewhere. - //AccentColour = columnColours[isSpecial], Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ } }; From 5c48d8931ad8dc5e7ba36cd06bcaf84052a77615 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 19:42:43 +0900 Subject: [PATCH 182/295] Add `StageDefinition` to `ManiaSkinConfigurationLookup` and make column background colour lookup work --- .../Argon/ManiaArgonSkinTransformer.cs | 24 +++++++++++++++++++ .../Legacy/HitTargetInsetContainer.cs | 5 ++-- .../Skinning/Legacy/LegacyColumnBackground.cs | 5 ++-- .../Skinning/Legacy/LegacyHitTarget.cs | 9 +++---- .../Legacy/LegacyManiaColumnElement.cs | 2 +- .../Legacy/LegacyManiaJudgementPiece.cs | 5 ++-- .../Skinning/Legacy/LegacyNotePiece.cs | 5 ++-- .../Skinning/Legacy/LegacyStageBackground.cs | 14 +++++------ .../Skinning/Legacy/LegacyStageForeground.cs | 5 ++-- .../Legacy/ManiaLegacySkinTransformer.cs | 6 ++--- .../Skinning/ManiaSkinConfigExtensions.cs | 8 ++++--- .../Skinning/ManiaSkinConfigurationLookup.cs | 16 +++++++++---- osu.Game.Rulesets.Mania/UI/Column.cs | 9 ++++--- osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 4 ++-- .../UI/Components/HitObjectArea.cs | 6 ++++- 15 files changed, 83 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index ec6aaf2ef7..57a317e87d 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -1,8 +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.Bindables; using osu.Framework.Graphics; +using osu.Framework.Utils; using osu.Game.Skinning; +using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Skinning.Argon { @@ -33,5 +36,26 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon return base.GetDrawableComponent(component); } + + public override IBindable? GetConfig(TLookup lookup) + { + if (lookup is ManiaSkinConfigurationLookup maniaLookup) + { + switch (maniaLookup.Lookup) + { + case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: + if (maniaLookup.StageDefinition.IsSpecialColumn(maniaLookup.ColumnIndex ?? 0)) + return SkinUtils.As(new Bindable(Color4.Yellow)); + + // TODO: Add actual colours. + return SkinUtils.As(new Bindable(new Color4(RNG.NextSingle() * 0.5f, RNG.NextSingle() * 0.5f, RNG.NextSingle() * 0.5f, 1))); + } + + return base.GetConfig(new LegacyManiaSkinConfigurationLookup(maniaLookup.StageDefinition.Columns, maniaLookup.Lookup, + maniaLookup.ColumnIndex)); + } + + return base.GetConfig(lookup); + } } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs index 362a265789..de6d048295 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; @@ -30,9 +31,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) { - hitPosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? Stage.HIT_TARGET_POSITION; + hitPosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitPosition, stageDefinition)?.Value ?? Stage.HIT_TARGET_POSITION; direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs index f35cedab08..08fa9b9a73 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -30,9 +31,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) { - string lightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LightImage)?.Value + string lightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LightImage, stageDefinition)?.Value ?? "mania-stage-light"; float lightPosition = GetColumnSkinConfig(skin, LegacyManiaSkinConfigurationLookups.LightPosition)?.Value diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs index 611dac30b3..9819f09e10 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -23,15 +24,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy private Container directionContainer; [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) { - string targetImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitTargetImage)?.Value + string targetImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitTargetImage, stageDefinition)?.Value ?? "mania-stage-hint"; - bool showJudgementLine = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ShowJudgementLine)?.Value + bool showJudgementLine = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ShowJudgementLine, stageDefinition)?.Value ?? true; - Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.JudgementLineColour)?.Value + Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.JudgementLineColour, stageDefinition)?.Value ?? Color4.White; InternalChild = directionContainer = new Container diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs index e227c80845..cfebad4add 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs @@ -42,6 +42,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } protected IBindable GetColumnSkinConfig(ISkin skin, LegacyManiaSkinConfigurationLookups lookup) - => skin.GetManiaSkinConfig(lookup, Column.Index); + => skin.GetManiaSkinConfig(lookup, stage, Column.Index); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs index d09a73a693..f9653dab15 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; @@ -32,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, StageDefinition stageDefinition) { - float? scorePosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ScorePosition)?.Value; + float? scorePosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ScorePosition, stageDefinition)?.Value; if (scorePosition != null) scorePosition -= Stage.HIT_TARGET_POSITION + 150; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs index 41e149ea2f..779e1a152c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -35,9 +36,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) { - minimumColumnWidth = skin.GetConfig(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.MinimumColumnWidth))?.Value; + minimumColumnWidth = skin.GetConfig(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.MinimumColumnWidth, stageDefinition))?.Value; InternalChild = directionContainer = new Container { diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs index d039551cd7..01c2b599fa 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs @@ -30,10 +30,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy [BackgroundDependencyLoader] private void load(ISkinSource skin, StageDefinition stageDefinition) { - string leftImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftStageImage)?.Value + string leftImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftStageImage, stageDefinition)?.Value ?? "mania-stage-left"; - string rightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightStageImage)?.Value + string rightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightStageImage, stageDefinition)?.Value ?? "mania-stage-right"; InternalChildren = new[] @@ -91,16 +91,16 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, StageDefinition stageDefinition) { - float leftLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftLineWidth, columnIndex)?.Value ?? 1; - float rightLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1; + float leftLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftLineWidth, stageDefinition, columnIndex)?.Value ?? 1; + float rightLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightLineWidth, stageDefinition, columnIndex)?.Value ?? 1; bool hasLeftLine = leftLineWidth > 0; bool hasRightLine = (rightLineWidth > 0 && skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m) || isLastColumn; - Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White; - Color4 backgroundColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, columnIndex)?.Value ?? Color4.Black; + Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnLineColour, stageDefinition, columnIndex)?.Value ?? Color4.White; + Color4 backgroundColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, columnIndex)?.Value ?? Color4.Black; InternalChildren = new Drawable[] { diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs index f7c611d551..57b76c5402 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -25,9 +26,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) { - string bottomImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.BottomStageImage)?.Value + string bottomImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.BottomStageImage, stageDefinition)?.Value ?? "mania-stage-bottom"; sprite = skin.GetAnimation(bottomImage, true, true)?.With(d => diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 5ecdf988f9..352b205c0b 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy isLegacySkin = new Lazy(() => GetConfig(SkinConfiguration.LegacySetting.Version) != null); hasKeyTexture = new Lazy(() => { - string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1"; + string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, new StageDefinition(), 0)?.Value ?? "mania-key1"; return this.GetAnimation(keyImage, true, true) != null; }); } @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy if (!hit_result_mapping.ContainsKey(result)) return null; - string filename = this.GetManiaSkinConfig(hit_result_mapping[result])?.Value + string filename = this.GetManiaSkinConfig(hit_result_mapping[result], new StageDefinition())?.Value ?? default_hit_result_skin_filenames[result]; var animation = this.GetAnimation(filename, true, true); @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy public override IBindable GetConfig(TLookup lookup) { if (lookup is ManiaSkinConfigurationLookup maniaLookup) - return base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.TargetColumn)); + return base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); return base.GetConfig(lookup); } diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs index 4d0c321116..541b679043 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs @@ -4,6 +4,7 @@ #nullable disable using osu.Framework.Bindables; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.Skinning @@ -15,9 +16,10 @@ namespace osu.Game.Rulesets.Mania.Skinning /// /// The skin from which configuration is retrieved. /// The value to retrieve. - /// If not null, denotes the index of the column to which the entry applies. - public static IBindable GetManiaSkinConfig(this ISkin skin, LegacyManiaSkinConfigurationLookups lookup, int? index = null) + /// The stage definition. + /// If not null, denotes the index of the column to which the entry applies. + public static IBindable GetManiaSkinConfig(this ISkin skin, LegacyManiaSkinConfigurationLookups lookup, StageDefinition stageDefinition, int? columnIndex = null) => skin.GetConfig( - new ManiaSkinConfigurationLookup(lookup, index)); + new ManiaSkinConfigurationLookup(lookup, stageDefinition, columnIndex)); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs index e9005a3da0..8008c988d4 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs @@ -3,6 +3,7 @@ #nullable disable +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI; using osu.Game.Skinning; @@ -15,21 +16,28 @@ namespace osu.Game.Rulesets.Mania.Skinning /// public readonly LegacyManiaSkinConfigurationLookups Lookup; + /// + /// The stage containing the component which is performing this lookup. + /// + public readonly StageDefinition StageDefinition; + /// /// The intended index for the configuration. /// May be null if the configuration does not apply to a . /// - public readonly int? TargetColumn; + public readonly int? ColumnIndex; /// /// Creates a new . /// /// The lookup value. - /// The intended index for the configuration. May be null if the configuration does not apply to a . - public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null) + /// The stage definition. + /// The intended index for the configuration. May be null if the configuration does not apply to a . + public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, StageDefinition stageDefinition, int? columnIndex = null) { Lookup = lookup; - TargetColumn = targetColumn; + StageDefinition = stageDefinition; + ColumnIndex = columnIndex; } } } diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index c0739f5ec3..75d6d4c206 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -10,10 +10,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -65,11 +66,9 @@ namespace osu.Game.Rulesets.Mania.UI } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, StageDefinition stageDefinition) { - // TODO: reimplement this somewhere. - AccentColour = skin.GetConfig() - var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; + AccentColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, Index)?.Value ?? Color4.Black; //AccentColour = columnColours[isSpecial], foreach (var obj in HitObjectContainer.Objects) diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index 871ec9f1a3..bde2308635 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -65,14 +65,14 @@ namespace osu.Game.Rulesets.Mania.UI if (i > 0) { float spacing = currentSkin.GetConfig( - new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, i - 1)) + new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, stageDefinition, i - 1)) ?.Value ?? Stage.COLUMN_SPACING; columns[i].Margin = new MarginPadding { Left = spacing }; } float? width = currentSkin.GetConfig( - new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i)) + new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, stageDefinition, i)) ?.Value; if (width == null) diff --git a/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs index 7f4b8eacde..69bf146951 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -19,6 +20,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components protected readonly IBindable Direction = new Bindable(); public readonly HitObjectContainer HitObjectContainer; + [Resolved] + private StageDefinition stageDefinition { get; set; } + public HitObjectArea(HitObjectContainer hitObjectContainer) { InternalChild = new Container @@ -49,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components protected virtual void UpdateHitPosition() { float hitPosition = CurrentSkin.GetConfig( - new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value + new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition, stageDefinition))?.Value ?? Stage.HIT_TARGET_POSITION; Padding = Direction.Value == ScrollingDirection.Up From 1a0b953846f2ac67acfd93f228cc64e7efcd666d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 19:49:07 +0900 Subject: [PATCH 183/295] Remove unnecessary `Beatmap` parameter in `ManiaLegacySkinTransformer` --- .../Skinning/ColumnTestContainer.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 11 +++-------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs index b8d1313af5..8f6a8c42b1 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning private readonly Column column; [Cached] - private readonly StageDefinition stageDefinition = new StageDefinition { Columns = 1 }; + private readonly StageDefinition stageDefinition = new StageDefinition { Columns = 2 }; public ColumnTestContainer(int column, ManiaAction action, bool showColumn = false) { diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index c6b20b1baf..746dda6733 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania switch (skin) { case LegacySkin: - return new ManiaLegacySkinTransformer(skin, beatmap); + return new ManiaLegacySkinTransformer(skin); case ArgonSkin: return new ManiaArgonSkinTransformer(skin); diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 352b205c0b..0a59c10b5f 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -9,7 +9,6 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Audio; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Scoring; @@ -19,8 +18,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { public class ManiaLegacySkinTransformer : LegacySkinTransformer { - private readonly ManiaBeatmap beatmap; - /// /// Mapping of to their corresponding /// value. @@ -59,15 +56,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy /// private readonly Lazy hasKeyTexture; - public ManiaLegacySkinTransformer(ISkin skin, IBeatmap beatmap) + public ManiaLegacySkinTransformer(ISkin skin) : base(skin) { - this.beatmap = (ManiaBeatmap)beatmap; - isLegacySkin = new Lazy(() => GetConfig(SkinConfiguration.LegacySetting.Version) != null); hasKeyTexture = new Lazy(() => { - string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, new StageDefinition(), 0)?.Value ?? "mania-key1"; + string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, new StageDefinition { Columns = 1 }, 0)?.Value ?? "mania-key1"; return this.GetAnimation(keyImage, true, true) != null; }); } @@ -149,7 +144,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy public override IBindable GetConfig(TLookup lookup) { if (lookup is ManiaSkinConfigurationLookup maniaLookup) - return base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); + return base.GetConfig(new LegacyManiaSkinConfigurationLookup(maniaLookup.StageDefinition.Columns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); return base.GetConfig(lookup); } From 5fe9b953a526447234ed3d83017bc8f259e99e7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Oct 2022 19:50:55 +0900 Subject: [PATCH 184/295] Add back triangles column colours via a transformer --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++ .../Argon/ManiaArgonSkinTransformer.cs | 3 -- .../Default/ManiaTrianglesSkinTransformer.cs | 42 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 746dda6733..7be4243ad6 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -27,6 +27,7 @@ using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Skinning.Argon; +using osu.Game.Rulesets.Mania.Skinning.Default; using osu.Game.Rulesets.Mania.Skinning.Legacy; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -67,6 +68,9 @@ namespace osu.Game.Rulesets.Mania { switch (skin) { + case TrianglesSkin: + return new ManiaTrianglesSkinTransformer(skin); + case LegacySkin: return new ManiaLegacySkinTransformer(skin); diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 57a317e87d..ea34d8d4c5 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -50,9 +50,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon // TODO: Add actual colours. return SkinUtils.As(new Bindable(new Color4(RNG.NextSingle() * 0.5f, RNG.NextSingle() * 0.5f, RNG.NextSingle() * 0.5f, 1))); } - - return base.GetConfig(new LegacyManiaSkinConfigurationLookup(maniaLookup.StageDefinition.Columns, maniaLookup.Lookup, - maniaLookup.ColumnIndex)); } return base.GetConfig(lookup); diff --git a/osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs new file mode 100644 index 0000000000..88f1f6ed26 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Default +{ + public class ManiaTrianglesSkinTransformer : SkinTransformer + { + public ManiaTrianglesSkinTransformer(ISkin skin) + : base(skin) + { + } + + private readonly Color4 colourEven = new Color4(6, 84, 0, 255); + private readonly Color4 colourOdd = new Color4(94, 0, 57, 255); + private readonly Color4 colourSpecial = new Color4(0, 48, 63, 255); + + public override IBindable? GetConfig(TLookup lookup) + { + if (lookup is ManiaSkinConfigurationLookup maniaLookup) + { + switch (maniaLookup.Lookup) + { + case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: + int column = maniaLookup.ColumnIndex ?? 0; + + if (maniaLookup.StageDefinition.IsSpecialColumn(column)) + return SkinUtils.As(new Bindable(colourSpecial)); + + int distanceToEdge = Math.Min(column, (maniaLookup.StageDefinition.Columns - 1) - column); + return SkinUtils.As(new Bindable(distanceToEdge % 2 == 0 ? colourOdd : colourEven)); + } + } + + return base.GetConfig(lookup); + } + } +} From 2ae1aef0be4252e8b3bc98d61eea057ad7083d14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 19:14:31 +0900 Subject: [PATCH 185/295] Move column initialisation to ctor and fix remaining tests --- .../Editor/ManiaPlacementBlueprintTestScene.cs | 4 ++++ .../Editor/ManiaSelectionBlueprintTestScene.cs | 2 +- .../Editor/TestSceneManiaBeatSnapGrid.cs | 6 +++--- .../Editor/TestSceneManiaComposeScreen.cs | 2 +- .../Editor/TestSceneManiaHitObjectComposer.cs | 2 +- .../ManiaLegacyReplayTest.cs | 6 +++--- .../ManiaSpecialColumnTest.cs | 5 +---- .../Mods/TestSceneManiaModHoldOff.cs | 2 +- .../Skinning/ColumnTestContainer.cs | 2 +- .../Skinning/ManiaSkinnableTestScene.cs | 4 ++++ .../Skinning/TestSceneBarLine.cs | 2 +- .../Skinning/TestScenePlayfield.cs | 6 +++--- .../Skinning/TestSceneStage.cs | 2 +- .../TestSceneAutoGeneration.cs | 14 +++++++------- osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs | 4 ++++ .../TestSceneDrawableManiaHitObject.cs | 5 +++++ .../TestSceneOutOfOrderHits.cs | 2 +- osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs | 2 +- .../TestSceneTimingBasedNoteColouring.cs | 2 +- .../Beatmaps/ManiaBeatmapConverter.cs | 4 ++-- .../Beatmaps/StageDefinition.cs | 11 ++++++++++- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 4 ++-- 22 files changed, 58 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs index 976efc51b2..a759e95d17 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; @@ -30,6 +31,9 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor [Cached(typeof(IScrollingInfo))] private IScrollingInfo scrollingInfo; + [Cached] + private readonly StageDefinition stage = new StageDefinition(5); + protected ManiaPlacementBlueprintTestScene() { scrollingInfo = ((ScrollingTestContainer)HitObjectContainer).ScrollingInfo; diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaSelectionBlueprintTestScene.cs index 679a15e8cb..4cadcf138b 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaSelectionBlueprintTestScene.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor protected ManiaSelectionBlueprintTestScene(int columns) { - var stageDefinitions = new List { new StageDefinition { Columns = columns } }; + var stageDefinitions = new List { new StageDefinition(columns) }; base.Content.Child = scrollingTestContainer = new ScrollingTestContainer(ScrollingDirection.Up) { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index ec96205067..ef140995ec 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo(); [Cached(typeof(EditorBeatmap))] - private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition()) + private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition(2)) { BeatmapInfo = { @@ -56,8 +56,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { Playfield = new ManiaPlayfield(new List { - new StageDefinition { Columns = 4 }, - new StageDefinition { Columns = 3 } + new StageDefinition(4), + new StageDefinition(3) }) { Clock = new FramedClock(new StopwatchClock()) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs index e96a186ae4..e082b90d3b 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { AddStep("setup compose screen", () => { - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 4 }) + var beatmap = new ManiaBeatmap(new StageDefinition(4)) { BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }, }; diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs index fcc9e2e6c3..a3985be936 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaHitObjectComposer.cs @@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { InternalChildren = new Drawable[] { - EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }) + EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition(4)) { BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo } }), diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.cs index b64006316e..7d1a934456 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaLegacyReplayTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests [TestCase(ManiaAction.Key8)] public void TestEncodeDecodeSingleStage(params ManiaAction[] actions) { - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 9 }); + var beatmap = new ManiaBeatmap(new StageDefinition(9)); var frame = new ManiaReplayFrame(0, actions); var legacyFrame = frame.ToLegacy(beatmap); @@ -38,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.Tests [TestCase(ManiaAction.Key8)] public void TestEncodeDecodeDualStage(params ManiaAction[] actions) { - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 5 }); - beatmap.Stages.Add(new StageDefinition { Columns = 5 }); + var beatmap = new ManiaBeatmap(new StageDefinition(5)); + beatmap.Stages.Add(new StageDefinition(5)); var frame = new ManiaReplayFrame(0, actions); var legacyFrame = frame.ToLegacy(beatmap); diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs index c83e8a51ed..3bd654e75e 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs @@ -35,10 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests }, 7)] public void Test(IEnumerable special, int columns) { - var definition = new StageDefinition - { - Columns = columns - }; + var definition = new StageDefinition(columns); var results = getResults(definition); Assert.AreEqual(special, results); } diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHoldOff.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHoldOff.cs index 7970d5b594..d27a79c41d 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHoldOff.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHoldOff.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods private static ManiaBeatmap createRawBeatmap() { - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); + var beatmap = new ManiaBeatmap(new StageDefinition(1)); beatmap.ControlPointInfo.Add(0.0, new TimingControlPoint { BeatLength = 1000 }); // Set BPM to 60 // Add test hit objects diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs index 8f6a8c42b1..6fdf32dfbe 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning private readonly Column column; [Cached] - private readonly StageDefinition stageDefinition = new StageDefinition { Columns = 2 }; + private readonly StageDefinition stageDefinition = new StageDefinition(5); public ColumnTestContainer(int column, ManiaAction action, bool showColumn = false) { diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs index 9f235689b4..2c5535a65f 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling.Algorithms; using osu.Game.Tests.Visual; @@ -24,6 +25,9 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [Cached(Type = typeof(IScrollingInfo))] private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo(); + [Cached] + private readonly StageDefinition stage = new StageDefinition(4); + protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset(); protected ManiaSkinnableTestScene() diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs index ff557638a9..1bfe55b074 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { var stageDefinitions = new List { - new StageDefinition { Columns = 4 }, + new StageDefinition(4), }; SetContents(_ => new ManiaPlayfield(stageDefinitions).With(s => diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs index 62dadbc3dd..9817719c94 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { stageDefinitions = new List { - new StageDefinition { Columns = 2 } + new StageDefinition(2) }; SetContents(_ => new ManiaPlayfield(stageDefinitions)); @@ -36,8 +36,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { stageDefinitions = new List { - new StageDefinition { Columns = 2 }, - new StageDefinition { Columns = 2 } + new StageDefinition(2), + new StageDefinition(2) }; SetContents(_ => new ManiaPlayfield(stageDefinitions)); diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs index f3f1b9416f..07aa0b845f 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning return new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4) { - Child = new Stage(0, new StageDefinition { Columns = 4 }, ref normalAction, ref specialAction) + Child = new Stage(0, new StageDefinition(4), ref normalAction, ref specialAction) }; }); } diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs index 21ec85bbe6..3abeb8a5f6 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | - | // | | - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); + var beatmap = new ManiaBeatmap(new StageDefinition(1)); beatmap.HitObjects.Add(new Note { StartTime = 1000 }); var generated = new ManiaAutoGenerator(beatmap).Generate(); @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | // | | - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); + var beatmap = new ManiaBeatmap(new StageDefinition(1)); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); var generated = new ManiaAutoGenerator(beatmap).Generate(); @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | - | - | // | | | - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + var beatmap = new ManiaBeatmap(new StageDefinition(2)); beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 }); @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | * | // | | | - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + var beatmap = new ManiaBeatmap(new StageDefinition(2)); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 }); @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | - | | // | | | - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + var beatmap = new ManiaBeatmap(new StageDefinition(2)); beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 }); @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | | // | | | - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + var beatmap = new ManiaBeatmap(new StageDefinition(2)); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 }); @@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | | // | | | - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); + var beatmap = new ManiaBeatmap(new StageDefinition(2)); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs index 8fff87ae78..3bac3c7650 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; @@ -28,6 +29,9 @@ namespace osu.Game.Rulesets.Mania.Tests [Cached(typeof(IReadOnlyList))] private IReadOnlyList mods { get; set; } = Array.Empty(); + [Cached] + private readonly StageDefinition stage = new StageDefinition(1); + private readonly List columns = new List(); public TestSceneColumn() diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs index bd34f1ef11..ea033082a7 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs @@ -4,11 +4,13 @@ #nullable disable using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI; @@ -24,6 +26,9 @@ namespace osu.Game.Rulesets.Mania.Tests private Column column; + [Cached] + private readonly StageDefinition stage = new StageDefinition(1); + [SetUp] public void SetUp() => Schedule(() => { diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs index a563dc3106..1f139b5b78 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Mania.Tests { AddStep("load player", () => { - Beatmap.Value = CreateWorkingBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }) + Beatmap.Value = CreateWorkingBeatmap(new ManiaBeatmap(new StageDefinition(4)) { HitObjects = hitObjects, BeatmapInfo = diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs index cf8947c1ed..6387dac957 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneStage.cs @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Tests { var specialAction = ManiaAction.Special1; - var stage = new Stage(0, new StageDefinition { Columns = 2 }, ref action, ref specialAction); + var stage = new Stage(0, new StageDefinition(2), ref action, ref specialAction); stages.Add(stage); return new ScrollingTestContainer(direction) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs index e84d02775a..9f2e3d2502 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.Tests { const double beat_length = 500; - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }) + var beatmap = new ManiaBeatmap(new StageDefinition(1)) { HitObjects = { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 90cd7f57b5..632b7cdcc7 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -93,10 +93,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected override Beatmap CreateBeatmap() { - beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }, originalTargetColumns); + beatmap = new ManiaBeatmap(new StageDefinition(TargetColumns), originalTargetColumns); if (Dual) - beatmap.Stages.Add(new StageDefinition { Columns = TargetColumns }); + beatmap.Stages.Add(new StageDefinition(TargetColumns)); return beatmap; } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs index 08ee97b32b..898b558eb3 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using osu.Game.Rulesets.Mania.UI; namespace osu.Game.Rulesets.Mania.Beatmaps @@ -15,7 +16,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// /// The number of s which this stage contains. /// - public int Columns; + public readonly int Columns; + + public StageDefinition(int columns) + { + if (columns < 1) + throw new ArgumentException("Column count must be above zero.", nameof(columns)); + + Columns = columns; + } /// /// Whether the column index is a special column for this stage. diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 0a59c10b5f..1c06f3cffb 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy isLegacySkin = new Lazy(() => GetConfig(SkinConfiguration.LegacySetting.Version) != null); hasKeyTexture = new Lazy(() => { - string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, new StageDefinition { Columns = 1 }, 0)?.Value ?? "mania-key1"; + string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, new StageDefinition(1), 0)?.Value ?? "mania-key1"; return this.GetAnimation(keyImage, true, true) != null; }); } @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy if (!hit_result_mapping.ContainsKey(result)) return null; - string filename = this.GetManiaSkinConfig(hit_result_mapping[result], new StageDefinition())?.Value + string filename = this.GetManiaSkinConfig(hit_result_mapping[result], new StageDefinition(1))?.Value ?? default_hit_result_skin_filenames[result]; var animation = this.GetAnimation(filename, true, true); From 3947011baf594cec7b6f7ab4984f22d75e5305b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 19:21:38 +0900 Subject: [PATCH 186/295] Fix regression in legacy dual stage handling logic --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 6 +++--- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 9 +++++++-- .../Skinning/LegacyManiaSkinConfigurationLookup.cs | 12 +++++++----- osu.Game/Skinning/LegacySkin.cs | 4 ++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 7be4243ad6..9d2182f276 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -71,11 +71,11 @@ namespace osu.Game.Rulesets.Mania case TrianglesSkin: return new ManiaTrianglesSkinTransformer(skin); - case LegacySkin: - return new ManiaLegacySkinTransformer(skin); - case ArgonSkin: return new ManiaArgonSkinTransformer(skin); + + case LegacySkin: + return new ManiaLegacySkinTransformer(skin, beatmap); } return null; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 1c06f3cffb..5a0478f025 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -9,6 +9,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Scoring; @@ -56,9 +57,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy /// private readonly Lazy hasKeyTexture; - public ManiaLegacySkinTransformer(ISkin skin) + private readonly ManiaBeatmap beatmap; + + public ManiaLegacySkinTransformer(ISkin skin, IBeatmap beatmap) : base(skin) { + this.beatmap = (ManiaBeatmap)beatmap; + isLegacySkin = new Lazy(() => GetConfig(SkinConfiguration.LegacySetting.Version) != null); hasKeyTexture = new Lazy(() => { @@ -144,7 +149,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy public override IBindable GetConfig(TLookup lookup) { if (lookup is ManiaSkinConfigurationLookup maniaLookup) - return base.GetConfig(new LegacyManiaSkinConfigurationLookup(maniaLookup.StageDefinition.Columns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); + return base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); return base.GetConfig(lookup); } diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs index 45454be4a5..8e786c96d9 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs @@ -1,19 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - namespace osu.Game.Skinning { public class LegacyManiaSkinConfigurationLookup { - public readonly int Keys; + /// + /// Total columns across all stages. + /// + public readonly int TotalColumns; + public readonly LegacyManiaSkinConfigurationLookups Lookup; public readonly int? TargetColumn; - public LegacyManiaSkinConfigurationLookup(int keys, LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null) + public LegacyManiaSkinConfigurationLookup(int totalColumns, LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null) { - Keys = keys; + TotalColumns = totalColumns; Lookup = lookup; TargetColumn = targetColumn; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1e096702b3..e1a630b643 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -128,8 +128,8 @@ namespace osu.Game.Skinning private IBindable? lookupForMania(LegacyManiaSkinConfigurationLookup maniaLookup) { - if (!maniaConfigurations.TryGetValue(maniaLookup.Keys, out var existing)) - maniaConfigurations[maniaLookup.Keys] = existing = new LegacyManiaSkinConfiguration(maniaLookup.Keys); + if (!maniaConfigurations.TryGetValue(maniaLookup.TotalColumns, out var existing)) + maniaConfigurations[maniaLookup.TotalColumns] = existing = new LegacyManiaSkinConfiguration(maniaLookup.TotalColumns); switch (maniaLookup.Lookup) { From 276395f1afebe3902aa57245a4ccddd65b52d1cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 19:51:18 +0900 Subject: [PATCH 187/295] Fix column colour not updating on skin change --- osu.Game.Rulesets.Mania/UI/Column.cs | 31 +++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 75d6d4c206..8c50ab919b 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -65,14 +65,17 @@ namespace osu.Game.Rulesets.Mania.UI HitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both }; } - [BackgroundDependencyLoader] - private void load(ISkinSource skin, StageDefinition stageDefinition) - { - AccentColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, Index)?.Value ?? Color4.Black; + [Resolved] + private ISkinSource skin { get; set; } - //AccentColour = columnColours[isSpecial], - foreach (var obj in HitObjectContainer.Objects) - obj.AccentColour.Value = AccentColour; + [Resolved] + private StageDefinition stageDefinition { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + skin.SourceChanged += onSourceChanged; + onSourceChanged(); Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { @@ -104,13 +107,25 @@ namespace osu.Game.Rulesets.Mania.UI RegisterPool(50, 250); } + private void onSourceChanged() + { + AccentColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, Index)?.Value ?? Color4.Black; + foreach (var obj in HitObjectContainer.Objects) + obj.AccentColour.Value = AccentColour; + } + protected override void LoadComplete() { base.LoadComplete(); - NewResult += OnNewResult; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + skin.SourceChanged += onSourceChanged; + } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); From 532d101080e04405149a43c873f99aa4ba10dd5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 19:58:39 +0900 Subject: [PATCH 188/295] Remove unused class --- .../UI/Components/ColumnBackground.cs | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs deleted file mode 100644 index 5bd2d3ab48..0000000000 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs +++ /dev/null @@ -1,110 +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 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.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Rulesets.UI.Scrolling; -using osuTK.Graphics; - -namespace osu.Game.Rulesets.Mania.UI.Components -{ - public class ColumnBackground : CompositeDrawable, IKeyBindingHandler, IHasAccentColour - { - private readonly IBindable action = new Bindable(); - - private Box background; - private Box backgroundOverlay; - - private readonly IBindable direction = new Bindable(); - - [BackgroundDependencyLoader] - private void load(IBindable action, IScrollingInfo scrollingInfo) - { - this.action.BindTo(action); - - InternalChildren = new[] - { - background = new Box - { - Name = "Background", - RelativeSizeAxes = Axes.Both, - }, - backgroundOverlay = new Box - { - Name = "Background Gradient Overlay", - RelativeSizeAxes = Axes.Both, - Height = 0.5f, - Blending = BlendingParameters.Additive, - Alpha = 0 - } - }; - - direction.BindTo(scrollingInfo.Direction); - direction.BindValueChanged(dir => - { - backgroundOverlay.Anchor = backgroundOverlay.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; - updateColours(); - }, true); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - updateColours(); - } - - private Color4 accentColour; - - public Color4 AccentColour - { - get => accentColour; - set - { - if (accentColour == value) - return; - - accentColour = value; - - updateColours(); - } - } - - private void updateColours() - { - if (!IsLoaded) - return; - - background.Colour = AccentColour.Darken(5); - - var brightPoint = AccentColour.Opacity(0.6f); - var dimPoint = AccentColour.Opacity(0); - - backgroundOverlay.Colour = ColourInfo.GradientVertical( - direction.Value == ScrollingDirection.Up ? brightPoint : dimPoint, - direction.Value == ScrollingDirection.Up ? dimPoint : brightPoint); - } - - public bool OnPressed(KeyBindingPressEvent e) - { - if (e.Action == action.Value) - backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint); - return false; - } - - public void OnReleased(KeyBindingReleaseEvent e) - { - if (e.Action == action.Value) - backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); - } - } -} From 6b79f164612c9d4a9608650d4954d82e06b84773 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 20:02:02 +0900 Subject: [PATCH 189/295] Make `Column.AccentColour` bindable --- .../ManiaPlacementBlueprintTestScene.cs | 2 +- .../Skinning/ColumnTestContainer.cs | 2 +- .../TestSceneColumn.cs | 2 +- .../TestSceneDrawableManiaHitObject.cs | 2 +- .../Skinning/Argon/ArgonKeyArea.cs | 23 +++--- osu.Game.Rulesets.Mania/UI/Column.cs | 15 ++-- .../UI/Components/DefaultColumnBackground.cs | 12 +++- .../UI/Components/DefaultHitTarget.cs | 16 +++-- .../UI/Components/DefaultKeyArea.cs | 20 ++++-- .../UI/DefaultHitExplosion.cs | 71 ++++++++++--------- 10 files changed, 101 insertions(+), 64 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs index a759e95d17..0e4f612999 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { Anchor = Anchor.Centre, Origin = Anchor.Centre, - AccentColour = Color4.OrangeRed, + AccentColour = { Value = Color4.OrangeRed }, Clock = new FramedClock(new StopwatchClock()), // No scroll }); } diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs index 6fdf32dfbe..cefcce8319 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning this.column = new Column(column, false) { Action = { Value = action }, - AccentColour = Color4.Orange, + AccentColour = { Value = Color4.Orange }, Alpha = showColumn ? 1 : 0 }, content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs index 3bac3c7650..83491b6fe9 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneColumn.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Mania.Tests Anchor = Anchor.Centre, Origin = Anchor.Centre, Height = 0.85f, - AccentColour = Color4.OrangeRed, + AccentColour = { Value = Color4.OrangeRed }, Action = { Value = action }, }; diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs index ea033082a7..d273f5cb35 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.Tests { Action = { Value = ManiaAction.Key1 }, Height = 0.85f, - AccentColour = Color4.Gray + AccentColour = { Value = Color4.Gray }, }, }; }); diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 0153cdbd68..9d16e84f1e 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private Container bottomIcon = null!; private CircularContainer topIcon = null!; + private Bindable accentColour = null!; + [Resolved] private Column column { get; set; } = null!; @@ -55,7 +57,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { Name = "Key gradient", RelativeSizeAxes = Axes.Both, - Colour = column.AccentColour.Darken(0.6f), }, hitTargetLine = new Circle { @@ -80,7 +81,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, Blending = BlendingParameters.Additive, - Colour = column.AccentColour, Y = icon_vertical_offset, Children = new[] { @@ -134,6 +134,13 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); + + accentColour = column.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => + { + background.Colour = colour.NewValue.Darken(0.6f); + bottomIcon.Colour = colour.NewValue; + }, true); } private void onDirectionChanged(ValueChangedEvent direction) @@ -159,11 +166,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon if (e.Action != column.Action.Value) return false; const double lighting_fade_in_duration = 50; - Color4 lightingColour = column.AccentColour.Lighten(0.9f); + Color4 lightingColour = accentColour.Value.Lighten(0.9f); background - .FadeColour(column.AccentColour.Lighten(0.4f), 40).Then() - .FadeColour(column.AccentColour, 150, Easing.OutQuint); + .FadeColour(accentColour.Value.Lighten(0.4f), 40).Then() + .FadeColour(accentColour.Value, 150, Easing.OutQuint); hitTargetLine.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint); hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters @@ -201,9 +208,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon if (e.Action != column.Action.Value) return; const double lighting_fade_out_duration = 300; - Color4 lightingColour = column.AccentColour.Lighten(0.9f).Opacity(0); + Color4 lightingColour = accentColour.Value.Lighten(0.9f).Opacity(0); - background.FadeColour(column.AccentColour.Darken(0.6f), lighting_fade_out_duration, Easing.OutQuint); + background.FadeColour(accentColour.Value.Darken(0.6f), lighting_fade_out_duration, Easing.OutQuint); topIcon.ScaleTo(1f, 200, Easing.OutQuint); topIcon.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters @@ -221,7 +228,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Radius = 30, }, lighting_fade_out_duration, Easing.OutQuint); - bottomIcon.FadeColour(column.AccentColour, lighting_fade_out_duration, Easing.OutQuint); + bottomIcon.FadeColour(accentColour.Value, lighting_fade_out_duration, Easing.OutQuint); foreach (var circle in bottomIcon) { diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 8c50ab919b..81d9ae28ab 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.UI /// public readonly bool IsSpecial; - public Color4 AccentColour { get; set; } + public readonly Bindable AccentColour = new Bindable(Color4.Black); public Column(int index, bool isSpecial) { @@ -77,6 +77,13 @@ namespace osu.Game.Rulesets.Mania.UI skin.SourceChanged += onSourceChanged; onSourceChanged(); + AccentColour.BindValueChanged(colour => + { + // Manual transfer as hit objects may be moved between column and unbinding is non-trivial. + foreach (var obj in HitObjectContainer.Objects) + obj.AccentColour.Value = colour.NewValue; + }, true); + Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both @@ -109,9 +116,7 @@ namespace osu.Game.Rulesets.Mania.UI private void onSourceChanged() { - AccentColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, Index)?.Value ?? Color4.Black; - foreach (var obj in HitObjectContainer.Objects) - obj.AccentColour.Value = AccentColour; + AccentColour.Value = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, Index)?.Value ?? Color4.Black; } protected override void LoadComplete() @@ -139,7 +144,7 @@ namespace osu.Game.Rulesets.Mania.UI DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)drawableHitObject; - maniaObject.AccentColour.Value = AccentColour; + maniaObject.AccentColour.Value = AccentColour.Value; maniaObject.CheckHittable = hitPolicy.IsHittable; } diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs index 39d17db6be..3680e7ea0a 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components [Resolved] private Column column { get; set; } + private Bindable accentColour; + public DefaultColumnBackground() { RelativeSizeAxes = Axes.Both; @@ -55,9 +57,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components } }; - background.Colour = column.AccentColour.Darken(5); - brightColour = column.AccentColour.Opacity(0.6f); - dimColour = column.AccentColour.Opacity(0); + accentColour = column.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => + { + background.Colour = colour.NewValue.Darken(5); + brightColour = colour.NewValue.Opacity(0.6f); + dimColour = colour.NewValue.Opacity(0); + }, true); direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs index 53fa86125f..97aa897782 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultHitTarget.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components private Container hitTargetLine; private Drawable hitTargetBar; + private Bindable accentColour; + [Resolved] private Column column { get; set; } @@ -54,12 +56,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components }, }; - hitTargetLine.EdgeEffect = new EdgeEffectParameters + accentColour = column.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => { - Type = EdgeEffectType.Glow, - Radius = 5, - Colour = column.AccentColour.Opacity(0.5f), - }; + hitTargetLine.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 5, + Colour = colour.NewValue.Opacity(0.5f), + }; + }, true); direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs index 5a0fab2ff4..6196850f91 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components private Container keyIcon; private Drawable gradient; + private Bindable accentColour; + [Resolved] private Column column { get; set; } @@ -75,15 +77,19 @@ namespace osu.Game.Rulesets.Mania.UI.Components } }; - keyIcon.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Radius = 5, - Colour = column.AccentColour.Opacity(0.5f), - }; - direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); + + accentColour = column.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => + { + keyIcon.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Radius = 5, + Colour = colour.NewValue.Opacity(0.5f), + }; + }); } private void onDirectionChanged(ValueChangedEvent direction) diff --git a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs index e83cd10d2d..59716ee3e2 100644 --- a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs @@ -32,6 +32,10 @@ namespace osu.Game.Rulesets.Mania.UI private CircularContainer largeFaint; private CircularContainer mainGlow1; + private CircularContainer mainGlow2; + private CircularContainer mainGlow3; + + private Bindable accentColour; public DefaultHitExplosion() { @@ -48,8 +52,6 @@ namespace osu.Game.Rulesets.Mania.UI const float roundness = 80; const float initial_height = 10; - var colour = Interpolation.ValueAt(0.4f, column.AccentColour, Color4.White, 0, 1); - InternalChildren = new Drawable[] { largeFaint = new CircularContainer @@ -61,13 +63,6 @@ namespace osu.Game.Rulesets.Mania.UI // we want our size to be very small so the glow dominates it. Size = new Vector2(default_large_faint_size), Blending = BlendingParameters.Additive, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.1f, column.AccentColour, Color4.White, 0, 1).Opacity(0.3f), - Roundness = 160, - Radius = 200, - }, }, mainGlow1 = new CircularContainer { @@ -76,15 +71,8 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Both, Masking = true, Blending = BlendingParameters.Additive, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.6f, column.AccentColour, Color4.White, 0, 1), - Roundness = 20, - Radius = 50, - }, }, - new CircularContainer + mainGlow2 = new CircularContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -93,15 +81,8 @@ namespace osu.Game.Rulesets.Mania.UI Size = new Vector2(0.01f, initial_height), Blending = BlendingParameters.Additive, Rotation = RNG.NextSingle(-angle_variance, angle_variance), - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = colour, - Roundness = roundness, - Radius = 40, - }, }, - new CircularContainer + mainGlow3 = new CircularContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -110,18 +91,44 @@ namespace osu.Game.Rulesets.Mania.UI Size = new Vector2(0.01f, initial_height), Blending = BlendingParameters.Additive, Rotation = RNG.NextSingle(-angle_variance, angle_variance), - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = colour, - Roundness = roundness, - Radius = 40, - }, } }; direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); + + accentColour = column.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => + { + largeFaint.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.1f, colour.NewValue, Color4.White, 0, 1).Opacity(0.3f), + Roundness = 160, + Radius = 200, + }; + mainGlow1.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.6f, colour.NewValue, Color4.White, 0, 1), + Roundness = 20, + Radius = 50, + }; + mainGlow2.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.4f, colour.NewValue, Color4.White, 0, 1), + Roundness = roundness, + Radius = 40, + }; + mainGlow3.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.4f, colour.NewValue, Color4.White, 0, 1), + Roundness = roundness, + Radius = 40, + }; + }, true); } private void onDirectionChanged(ValueChangedEvent direction) From 4a127f5d8123d5ed782505c1f3d9ae8ae5817a23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Oct 2022 22:41:20 +0900 Subject: [PATCH 190/295] Fix classic skin colours sourcing from triangles defaults --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 3 ++ .../Legacy/ManiaClassicSkinTransformer.cs | 38 +++++++++++++++++++ .../Legacy/ManiaLegacySkinTransformer.cs | 17 ++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaClassicSkinTransformer.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 9d2182f276..c4a8b7c8fa 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -74,6 +74,9 @@ namespace osu.Game.Rulesets.Mania case ArgonSkin: return new ManiaArgonSkinTransformer(skin); + case DefaultLegacySkin: + return new ManiaClassicSkinTransformer(skin, beatmap); + case LegacySkin: return new ManiaLegacySkinTransformer(skin, beatmap); } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaClassicSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaClassicSkinTransformer.cs new file mode 100644 index 0000000000..e57927897c --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaClassicSkinTransformer.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Legacy +{ + public class ManiaClassicSkinTransformer : ManiaLegacySkinTransformer + { + public ManiaClassicSkinTransformer(ISkin skin, IBeatmap beatmap) + : base(skin, beatmap) + { + } + + public override IBindable GetConfig(TLookup lookup) + { + if (lookup is ManiaSkinConfigurationLookup maniaLookup) + { + var baseLookup = base.GetConfig(lookup); + + if (baseLookup != null) + return baseLookup; + + // default provisioning. + switch (maniaLookup.Lookup) + { + case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: + return SkinUtils.As(new Bindable(Color4.Black)); + } + } + + return base.GetConfig(lookup); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 5a0478f025..759a1c2b38 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -8,12 +8,14 @@ using System.Collections.Generic; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; +using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Skinning.Legacy { @@ -149,7 +151,20 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy public override IBindable GetConfig(TLookup lookup) { if (lookup is ManiaSkinConfigurationLookup maniaLookup) - return base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); + { + var legacyLookup = + base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); + + if (legacyLookup != null) + return legacyLookup; + + // default legacy fallback. + switch (maniaLookup.Lookup) + { + case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: + return SkinUtils.As(new Bindable(Color4.Black)); + } + } return base.GetConfig(lookup); } From 0d21c0e49c682040034b97ee640cfbcbf6252403 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 14:10:39 +0900 Subject: [PATCH 191/295] Remove `StageDefinition` from configuration lookups I added this for future usage, but it turns out I can get the definitions directly from `ManiaBeatmap`. --- .../Skinning/Legacy/HitTargetInsetContainer.cs | 5 ++--- .../Skinning/Legacy/LegacyColumnBackground.cs | 5 ++--- .../Skinning/Legacy/LegacyHitTarget.cs | 9 ++++----- .../Skinning/Legacy/LegacyManiaColumnElement.cs | 2 +- .../Skinning/Legacy/LegacyManiaJudgementPiece.cs | 5 ++--- .../Skinning/Legacy/LegacyNotePiece.cs | 5 ++--- .../Skinning/Legacy/LegacyStageBackground.cs | 14 +++++++------- .../Skinning/Legacy/LegacyStageForeground.cs | 5 ++--- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 4 ++-- .../Skinning/ManiaSkinConfigExtensions.cs | 6 ++---- .../Skinning/ManiaSkinConfigurationLookup.cs | 10 +--------- osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 4 ++-- .../UI/Components/HitObjectArea.cs | 6 +----- 13 files changed, 30 insertions(+), 50 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs index de6d048295..362a265789 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/HitTargetInsetContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; @@ -31,9 +30,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo) { - hitPosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitPosition, stageDefinition)?.Value ?? Stage.HIT_TARGET_POSITION; + hitPosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? Stage.HIT_TARGET_POSITION; direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs index 08fa9b9a73..f35cedab08 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -31,9 +30,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo) { - string lightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LightImage, stageDefinition)?.Value + string lightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LightImage)?.Value ?? "mania-stage-light"; float lightPosition = GetColumnSkinConfig(skin, LegacyManiaSkinConfigurationLookups.LightPosition)?.Value diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs index 9819f09e10..611dac30b3 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitTarget.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -24,15 +23,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy private Container directionContainer; [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo) { - string targetImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitTargetImage, stageDefinition)?.Value + string targetImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitTargetImage)?.Value ?? "mania-stage-hint"; - bool showJudgementLine = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ShowJudgementLine, stageDefinition)?.Value + bool showJudgementLine = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ShowJudgementLine)?.Value ?? true; - Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.JudgementLineColour, stageDefinition)?.Value + Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.JudgementLineColour)?.Value ?? Color4.White; InternalChild = directionContainer = new Container diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs index cfebad4add..e227c80845 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs @@ -42,6 +42,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } protected IBindable GetColumnSkinConfig(ISkin skin, LegacyManiaSkinConfigurationLookups lookup) - => skin.GetManiaSkinConfig(lookup, stage, Column.Index); + => skin.GetManiaSkinConfig(lookup, Column.Index); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs index f9653dab15..d09a73a693 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaJudgementPiece.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; @@ -33,9 +32,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, StageDefinition stageDefinition) + private void load(ISkinSource skin) { - float? scorePosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ScorePosition, stageDefinition)?.Value; + float? scorePosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ScorePosition)?.Value; if (scorePosition != null) scorePosition -= Stage.HIT_TARGET_POSITION + 150; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs index 779e1a152c..41e149ea2f 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -36,9 +35,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo) { - minimumColumnWidth = skin.GetConfig(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.MinimumColumnWidth, stageDefinition))?.Value; + minimumColumnWidth = skin.GetConfig(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.MinimumColumnWidth))?.Value; InternalChild = directionContainer = new Container { diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs index 01c2b599fa..d039551cd7 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs @@ -30,10 +30,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy [BackgroundDependencyLoader] private void load(ISkinSource skin, StageDefinition stageDefinition) { - string leftImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftStageImage, stageDefinition)?.Value + string leftImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftStageImage)?.Value ?? "mania-stage-left"; - string rightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightStageImage, stageDefinition)?.Value + string rightImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightStageImage)?.Value ?? "mania-stage-right"; InternalChildren = new[] @@ -91,16 +91,16 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, StageDefinition stageDefinition) + private void load(ISkinSource skin) { - float leftLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftLineWidth, stageDefinition, columnIndex)?.Value ?? 1; - float rightLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightLineWidth, stageDefinition, columnIndex)?.Value ?? 1; + float leftLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.LeftLineWidth, columnIndex)?.Value ?? 1; + float rightLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1; bool hasLeftLine = leftLineWidth > 0; bool hasRightLine = (rightLineWidth > 0 && skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m) || isLastColumn; - Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnLineColour, stageDefinition, columnIndex)?.Value ?? Color4.White; - Color4 backgroundColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, columnIndex)?.Value ?? Color4.Black; + Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White; + Color4 backgroundColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, columnIndex)?.Value ?? Color4.Black; InternalChildren = new Drawable[] { diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs index 57b76c5402..f7c611d551 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageForeground.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -26,9 +25,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, IScrollingInfo scrollingInfo, StageDefinition stageDefinition) + private void load(ISkinSource skin, IScrollingInfo scrollingInfo) { - string bottomImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.BottomStageImage, stageDefinition)?.Value + string bottomImage = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.BottomStageImage)?.Value ?? "mania-stage-bottom"; sprite = skin.GetAnimation(bottomImage, true, true)?.With(d => diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 759a1c2b38..8dc81f2101 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy isLegacySkin = new Lazy(() => GetConfig(SkinConfiguration.LegacySetting.Version) != null); hasKeyTexture = new Lazy(() => { - string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, new StageDefinition(1), 0)?.Value ?? "mania-key1"; + string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1"; return this.GetAnimation(keyImage, true, true) != null; }); } @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy if (!hit_result_mapping.ContainsKey(result)) return null; - string filename = this.GetManiaSkinConfig(hit_result_mapping[result], new StageDefinition(1))?.Value + string filename = this.GetManiaSkinConfig(hit_result_mapping[result])?.Value ?? default_hit_result_skin_filenames[result]; var animation = this.GetAnimation(filename, true, true); diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs index 541b679043..e22bf63049 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigExtensions.cs @@ -4,7 +4,6 @@ #nullable disable using osu.Framework.Bindables; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.Skinning @@ -16,10 +15,9 @@ namespace osu.Game.Rulesets.Mania.Skinning /// /// The skin from which configuration is retrieved. /// The value to retrieve. - /// The stage definition. /// If not null, denotes the index of the column to which the entry applies. - public static IBindable GetManiaSkinConfig(this ISkin skin, LegacyManiaSkinConfigurationLookups lookup, StageDefinition stageDefinition, int? columnIndex = null) + public static IBindable GetManiaSkinConfig(this ISkin skin, LegacyManiaSkinConfigurationLookups lookup, int? columnIndex = null) => skin.GetConfig( - new ManiaSkinConfigurationLookup(lookup, stageDefinition, columnIndex)); + new ManiaSkinConfigurationLookup(lookup, columnIndex)); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs index 8008c988d4..4ec18f9b01 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs @@ -3,7 +3,6 @@ #nullable disable -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI; using osu.Game.Skinning; @@ -16,11 +15,6 @@ namespace osu.Game.Rulesets.Mania.Skinning /// public readonly LegacyManiaSkinConfigurationLookups Lookup; - /// - /// The stage containing the component which is performing this lookup. - /// - public readonly StageDefinition StageDefinition; - /// /// The intended index for the configuration. /// May be null if the configuration does not apply to a . @@ -31,12 +25,10 @@ namespace osu.Game.Rulesets.Mania.Skinning /// Creates a new . /// /// The lookup value. - /// The stage definition. /// The intended index for the configuration. May be null if the configuration does not apply to a . - public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, StageDefinition stageDefinition, int? columnIndex = null) + public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, int? columnIndex = null) { Lookup = lookup; - StageDefinition = stageDefinition; ColumnIndex = columnIndex; } } diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index bde2308635..871ec9f1a3 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -65,14 +65,14 @@ namespace osu.Game.Rulesets.Mania.UI if (i > 0) { float spacing = currentSkin.GetConfig( - new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, stageDefinition, i - 1)) + new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, i - 1)) ?.Value ?? Stage.COLUMN_SPACING; columns[i].Margin = new MarginPadding { Left = spacing }; } float? width = currentSkin.GetConfig( - new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, stageDefinition, i)) + new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i)) ?.Value; if (width == null) diff --git a/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs index 69bf146951..7f4b8eacde 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/HitObjectArea.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -20,9 +19,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components protected readonly IBindable Direction = new Bindable(); public readonly HitObjectContainer HitObjectContainer; - [Resolved] - private StageDefinition stageDefinition { get; set; } - public HitObjectArea(HitObjectContainer hitObjectContainer) { InternalChild = new Container @@ -53,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components protected virtual void UpdateHitPosition() { float hitPosition = CurrentSkin.GetConfig( - new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition, stageDefinition))?.Value + new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value ?? Stage.HIT_TARGET_POSITION; Padding = Direction.Value == ScrollingDirection.Up From 13e0a59f707e38144bdfc709121f220f1196b360 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 14:19:00 +0900 Subject: [PATCH 192/295] Add note about why `LegacyManiaSkinConfigurationLookup` exist --- osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs index 8e786c96d9..6f934d1a92 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs @@ -3,6 +3,11 @@ namespace osu.Game.Skinning { + /// + /// This class exists for the explicit purpose of ferrying information from ManiaBeatmap in a way LegacySkin can use it. + /// This is because half of the mania legacy skin implementation is in LegacySkin (osu.Game project) which doesn't have visibility + /// over ManiaBeatmap / StageDefinition. + /// public class LegacyManiaSkinConfigurationLookup { /// @@ -10,9 +15,10 @@ namespace osu.Game.Skinning /// public readonly int TotalColumns; - public readonly LegacyManiaSkinConfigurationLookups Lookup; public readonly int? TargetColumn; + public readonly LegacyManiaSkinConfigurationLookups Lookup; + public LegacyManiaSkinConfigurationLookup(int totalColumns, LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null) { TotalColumns = totalColumns; From eea3d5adb827c40bbc1c46cb38011623e1e32a5a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 14:26:29 +0900 Subject: [PATCH 193/295] Standardise column index naming and xmldoc --- .../Skinning/ManiaSkinConfigurationLookup.cs | 3 +- .../LegacyManiaSkinConfigurationLookup.cs | 11 +++- osu.Game/Skinning/LegacySkin.cs | 64 +++++++++---------- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs index 4ec18f9b01..59188f02f9 100644 --- a/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs +++ b/osu.Game.Rulesets.Mania/Skinning/ManiaSkinConfigurationLookup.cs @@ -16,8 +16,9 @@ namespace osu.Game.Rulesets.Mania.Skinning public readonly LegacyManiaSkinConfigurationLookups Lookup; /// - /// The intended index for the configuration. + /// The column which is being looked up. /// May be null if the configuration does not apply to a . + /// Note that this is the absolute index across all stages. /// public readonly int? ColumnIndex; diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs index 6f934d1a92..c9e96ab3f1 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs @@ -15,15 +15,20 @@ namespace osu.Game.Skinning /// public readonly int TotalColumns; - public readonly int? TargetColumn; + /// + /// The column which is being looked up. + /// May be null if the configuration does not apply to a specific column. + /// Note that this is the absolute index across all stages. + /// + public readonly int? ColumnIndex; public readonly LegacyManiaSkinConfigurationLookups Lookup; - public LegacyManiaSkinConfigurationLookup(int totalColumns, LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null) + public LegacyManiaSkinConfigurationLookup(int totalColumns, LegacyManiaSkinConfigurationLookups lookup, int? columnIndex = null) { TotalColumns = totalColumns; Lookup = lookup; - TargetColumn = targetColumn; + ColumnIndex = columnIndex; } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e1a630b643..646746a0f3 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -134,12 +134,12 @@ namespace osu.Game.Skinning switch (maniaLookup.Lookup) { case LegacyManiaSkinConfigurationLookups.ColumnWidth: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(new Bindable(existing.ColumnWidth[maniaLookup.TargetColumn.Value])); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(new Bindable(existing.ColumnWidth[maniaLookup.ColumnIndex.Value])); case LegacyManiaSkinConfigurationLookups.ColumnSpacing: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(new Bindable(existing.ColumnSpacing[maniaLookup.TargetColumn.Value])); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(new Bindable(existing.ColumnSpacing[maniaLookup.ColumnIndex.Value])); case LegacyManiaSkinConfigurationLookups.HitPosition: return SkinUtils.As(new Bindable(existing.HitPosition)); @@ -157,15 +157,15 @@ namespace osu.Game.Skinning return SkinUtils.As(getManiaImage(existing, "LightingN")); case LegacyManiaSkinConfigurationLookups.ExplosionScale: - Debug.Assert(maniaLookup.TargetColumn != null); + Debug.Assert(maniaLookup.ColumnIndex != null); if (GetConfig(SkinConfiguration.LegacySetting.Version)?.Value < 2.5m) return SkinUtils.As(new Bindable(1)); - if (existing.ExplosionWidth[maniaLookup.TargetColumn.Value] != 0) - return SkinUtils.As(new Bindable(existing.ExplosionWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); + if (existing.ExplosionWidth[maniaLookup.ColumnIndex.Value] != 0) + return SkinUtils.As(new Bindable(existing.ExplosionWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); - return SkinUtils.As(new Bindable(existing.ColumnWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); + return SkinUtils.As(new Bindable(existing.ColumnWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); case LegacyManiaSkinConfigurationLookups.ColumnLineColour: return SkinUtils.As(getCustomColour(existing, "ColourColumnLine")); @@ -174,53 +174,53 @@ namespace osu.Game.Skinning return SkinUtils.As(getCustomColour(existing, "ColourJudgementLine")); case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getCustomColour(existing, $"Colour{maniaLookup.TargetColumn + 1}")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getCustomColour(existing, $"Colour{maniaLookup.ColumnIndex + 1}")); case LegacyManiaSkinConfigurationLookups.ColumnLightColour: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getCustomColour(existing, $"ColourLight{maniaLookup.TargetColumn + 1}")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getCustomColour(existing, $"ColourLight{maniaLookup.ColumnIndex + 1}")); case LegacyManiaSkinConfigurationLookups.MinimumColumnWidth: return SkinUtils.As(new Bindable(existing.MinimumColumnWidth)); case LegacyManiaSkinConfigurationLookups.NoteImage: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}")); case LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}H")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}H")); case LegacyManiaSkinConfigurationLookups.HoldNoteTailImage: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}T")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}T")); case LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}L")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}L")); case LegacyManiaSkinConfigurationLookups.HoldNoteLightImage: return SkinUtils.As(getManiaImage(existing, "LightingL")); case LegacyManiaSkinConfigurationLookups.HoldNoteLightScale: - Debug.Assert(maniaLookup.TargetColumn != null); + Debug.Assert(maniaLookup.ColumnIndex != null); if (GetConfig(SkinConfiguration.LegacySetting.Version)?.Value < 2.5m) return SkinUtils.As(new Bindable(1)); - if (existing.HoldNoteLightWidth[maniaLookup.TargetColumn.Value] != 0) - return SkinUtils.As(new Bindable(existing.HoldNoteLightWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); + if (existing.HoldNoteLightWidth[maniaLookup.ColumnIndex.Value] != 0) + return SkinUtils.As(new Bindable(existing.HoldNoteLightWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); - return SkinUtils.As(new Bindable(existing.ColumnWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); + return SkinUtils.As(new Bindable(existing.ColumnWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE)); case LegacyManiaSkinConfigurationLookups.KeyImage: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getManiaImage(existing, $"KeyImage{maniaLookup.ColumnIndex}")); case LegacyManiaSkinConfigurationLookups.KeyImageDown: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}D")); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(getManiaImage(existing, $"KeyImage{maniaLookup.ColumnIndex}D")); case LegacyManiaSkinConfigurationLookups.LeftStageImage: return SkinUtils.As(getManiaImage(existing, "StageLeft")); @@ -238,12 +238,12 @@ namespace osu.Game.Skinning return SkinUtils.As(getManiaImage(existing, "StageHint")); case LegacyManiaSkinConfigurationLookups.LeftLineWidth: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value])); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.ColumnIndex.Value])); case LegacyManiaSkinConfigurationLookups.RightLineWidth: - Debug.Assert(maniaLookup.TargetColumn != null); - return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value + 1])); + Debug.Assert(maniaLookup.ColumnIndex != null); + return SkinUtils.As(new Bindable(existing.ColumnLineWidth[maniaLookup.ColumnIndex.Value + 1])); case LegacyManiaSkinConfigurationLookups.Hit0: case LegacyManiaSkinConfigurationLookups.Hit50: From dee01abab18c293eebb3320013bd2badbc9a531e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 14:51:17 +0900 Subject: [PATCH 194/295] Add method to get stage from column index --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs | 14 ++++++++++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++-- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 12 ++++++++++-- .../Default/ManiaTrianglesSkinTransformer.cs | 13 ++++++++++--- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 1 - osu.Game.Rulesets.Mania/UI/Column.cs | 6 +----- 6 files changed, 37 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs index 4879ce6748..b5655a4579 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -60,5 +61,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps }, }; } + + public StageDefinition GetStageForColumnIndex(int column) + { + foreach (var stage in Stages) + { + if (column < stage.Columns) + return stage; + + column -= stage.Columns; + } + + throw new ArgumentOutOfRangeException(nameof(column), "Provided index exceeds all available stages"); + } } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index c4a8b7c8fa..6162184c9a 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mania switch (skin) { case TrianglesSkin: - return new ManiaTrianglesSkinTransformer(skin); + return new ManiaTrianglesSkinTransformer(skin, beatmap); case ArgonSkin: - return new ManiaArgonSkinTransformer(skin); + return new ManiaArgonSkinTransformer(skin, beatmap); case DefaultLegacySkin: return new ManiaClassicSkinTransformer(skin, beatmap); diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index ea34d8d4c5..80dc3978df 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -4,6 +4,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Skinning; using osuTK.Graphics; @@ -11,9 +13,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { public class ManiaArgonSkinTransformer : SkinTransformer { - public ManiaArgonSkinTransformer(ISkin skin) + private readonly ManiaBeatmap beatmap; + + public ManiaArgonSkinTransformer(ISkin skin, IBeatmap beatmap) : base(skin) { + this.beatmap = (ManiaBeatmap)beatmap; } public override Drawable? GetDrawableComponent(ISkinComponent component) @@ -44,7 +49,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon switch (maniaLookup.Lookup) { case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: - if (maniaLookup.StageDefinition.IsSpecialColumn(maniaLookup.ColumnIndex ?? 0)) + int column = maniaLookup.ColumnIndex ?? 0; + var stage = beatmap.GetStageForColumnIndex(column); + + if (stage.IsSpecialColumn(column)) return SkinUtils.As(new Bindable(Color4.Yellow)); // TODO: Add actual colours. diff --git a/osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs index 88f1f6ed26..eb51179cea 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Default/ManiaTrianglesSkinTransformer.cs @@ -3,6 +3,8 @@ using System; using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Skinning; using osuTK.Graphics; @@ -10,9 +12,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Default { public class ManiaTrianglesSkinTransformer : SkinTransformer { - public ManiaTrianglesSkinTransformer(ISkin skin) + private readonly ManiaBeatmap beatmap; + + public ManiaTrianglesSkinTransformer(ISkin skin, IBeatmap beatmap) : base(skin) { + this.beatmap = (ManiaBeatmap)beatmap; } private readonly Color4 colourEven = new Color4(6, 84, 0, 255); @@ -28,10 +33,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Default case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: int column = maniaLookup.ColumnIndex ?? 0; - if (maniaLookup.StageDefinition.IsSpecialColumn(column)) + var stage = beatmap.GetStageForColumnIndex(column); + + if (stage.IsSpecialColumn(column)) return SkinUtils.As(new Bindable(colourSpecial)); - int distanceToEdge = Math.Min(column, (maniaLookup.StageDefinition.Columns - 1) - column); + int distanceToEdge = Math.Min(column, (stage.Columns - 1) - column); return SkinUtils.As(new Bindable(distanceToEdge % 2 == 0 ? colourOdd : colourEven)); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 8dc81f2101..31cfcf9e16 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 81d9ae28ab..a622b8a155 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Skinning; @@ -68,9 +67,6 @@ namespace osu.Game.Rulesets.Mania.UI [Resolved] private ISkinSource skin { get; set; } - [Resolved] - private StageDefinition stageDefinition { get; set; } - [BackgroundDependencyLoader] private void load() { @@ -116,7 +112,7 @@ namespace osu.Game.Rulesets.Mania.UI private void onSourceChanged() { - AccentColour.Value = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, stageDefinition, Index)?.Value ?? Color4.Black; + AccentColour.Value = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, Index)?.Value ?? Color4.Black; } protected override void LoadComplete() From 9a92ff1681066b8ea9dfdb030b8674c5d241e79a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 16:24:43 +0900 Subject: [PATCH 195/295] Fix `SourceChanged` unbind directionality and add null check --- osu.Game.Rulesets.Mania/UI/Column.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index a622b8a155..89413f0f1b 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -124,7 +124,9 @@ namespace osu.Game.Rulesets.Mania.UI protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - skin.SourceChanged += onSourceChanged; + + if (skin != null) + skin.SourceChanged -= onSourceChanged; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From f1ea61b1a2bd1d277c9efa277075dd5e5d0a15df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 17:36:06 +0900 Subject: [PATCH 196/295] Remove unused colour code --- osu.Game.Rulesets.Mania/UI/Stage.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index 52c8829df3..c4cbb74e57 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -43,13 +43,6 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Drawable barLineContainer; - // private readonly Dictionary columnColours = new Dictionary - // { - // { ColumnType.Even, new Color4(6, 84, 0, 255) }, - // { ColumnType.Odd, new Color4(94, 0, 57, 255) }, - // { ColumnType.Special, new Color4(0, 48, 63, 255) } - // }; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Columns.Any(c => c.ReceivePositionalInputAt(screenSpacePos)); private readonly int firstColumnIndex; From 6f8533ef6bc78450ef48fc74c3b64c0c8ea85102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 16:51:41 +0900 Subject: [PATCH 197/295] Remove pointless colour specifications in test scenes --- osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs | 2 -- .../Skinning/ManiaHitObjectTestScene.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs index cefcce8319..d3e90170b2 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ColumnTestContainer.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.UI; -using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests.Skinning { @@ -34,7 +33,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning this.column = new Column(column, false) { Action = { Value = action }, - AccentColour = { Value = Color4.Orange }, Alpha = showColumn ? 1 : 0 }, content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs index fd82041ad8..75175c43d8 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs @@ -61,7 +61,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning c.Add(CreateHitObject().With(h => { h.HitObject.StartTime = Time.Current + 5000; - h.AccentColour.Value = Color4.Orange; })); }) }, From c1cb62cc35d75588f7462edceba8252762e3dc68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 16:54:08 +0900 Subject: [PATCH 198/295] Add basic argon note piece --- .../Skinning/Argon/ArgonHitTarget.cs | 3 +- .../Skinning/Argon/ArgonNotePiece.cs | 110 ++++++++++++++++++ .../Argon/ManiaArgonSkinTransformer.cs | 4 + 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs index 4750118583..7d7ef4a15e 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Mania.Skinning.Default; using osu.Game.Rulesets.UI.Scrolling; using osuTK.Graphics; @@ -20,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon private void load(IScrollingInfo scrollingInfo) { RelativeSizeAxes = Axes.X; - Height = DefaultNotePiece.NOTE_HEIGHT; + Height = ArgonNotePiece.NOTE_HEIGHT; InternalChildren = new[] { diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs new file mode 100644 index 0000000000..f560d2676c --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs @@ -0,0 +1,110 @@ +// 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.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + internal class ArgonNotePiece : CompositeDrawable + { + public const float NOTE_HEIGHT = 42; + + public const float CORNER_RADIUS = 3; + + private readonly IBindable direction = new Bindable(); + private readonly IBindable accentColour = new Bindable(); + + private readonly Box colouredBox; + private readonly Box shadow; + + public ArgonNotePiece() + { + RelativeSizeAxes = Axes.X; + Height = NOTE_HEIGHT; + + CornerRadius = CORNER_RADIUS; + Masking = true; + + InternalChildren = new Drawable[] + { + shadow = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = 0.82f, + Masking = true, + CornerRadius = CORNER_RADIUS, + Children = new Drawable[] + { + colouredBox = new Box + { + RelativeSizeAxes = Axes.Both, + } + } + }, + new Circle + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = CORNER_RADIUS * 2, + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 4, + Icon = FontAwesome.Solid.AngleDown, + Size = new Vector2(20), + Scale = new Vector2(1, 0.7f) + } + }; + } + + [BackgroundDependencyLoader(true)] + private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject) + { + direction.BindTo(scrollingInfo.Direction); + direction.BindValueChanged(onDirectionChanged, true); + + if (drawableObject != null) + { + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(onAccentChanged, true); + } + } + + private void onDirectionChanged(ValueChangedEvent direction) + { + colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up + ? Anchor.TopCentre + : Anchor.BottomCentre; + } + + private void onAccentChanged(ValueChangedEvent accent) + { + colouredBox.Colour = ColourInfo.GradientVertical( + accent.NewValue.Lighten(0.1f), + accent.NewValue + ); + + shadow.Colour = accent.NewValue.Darken(0.5f); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 80dc3978df..d280a16113 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -29,6 +29,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. switch (maniaComponent.Component) { + case ManiaSkinComponents.HoldNoteHead: + case ManiaSkinComponents.Note: + return new ArgonNotePiece(); + case ManiaSkinComponents.HitTarget: return new ArgonHitTarget(); From a10f9ebfa5b71039da9083464bf55f77d7784be3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 16:54:32 +0900 Subject: [PATCH 199/295] Update argon colours to roughly match design spec --- .../Argon/ManiaArgonSkinTransformer.cs | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index d280a16113..4843621a1e 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -3,7 +3,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Skinning; @@ -56,11 +55,49 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon int column = maniaLookup.ColumnIndex ?? 0; var stage = beatmap.GetStageForColumnIndex(column); - if (stage.IsSpecialColumn(column)) - return SkinUtils.As(new Bindable(Color4.Yellow)); + Color4 colour; - // TODO: Add actual colours. - return SkinUtils.As(new Bindable(new Color4(RNG.NextSingle() * 0.5f, RNG.NextSingle() * 0.5f, RNG.NextSingle() * 0.5f, 1))); + if (stage.IsSpecialColumn(column)) + colour = new Color4(159, 101, 255, 255); + else + { + switch (column % 8) + { + default: + colour = new Color4(240, 216, 0, 255); + break; + + case 1: + colour = new Color4(240, 101, 0, 255); + break; + + case 2: + colour = new Color4(240, 0, 130, 255); + break; + + case 3: + colour = new Color4(192, 0, 240, 255); + break; + + case 4: + colour = new Color4(178, 0, 240, 255); + break; + + case 5: + colour = new Color4(0, 96, 240, 255); + break; + + case 6: + colour = new Color4(0, 226, 240, 255); + break; + + case 7: + colour = new Color4(0, 240, 96, 255); + break; + } + } + + return SkinUtils.As(new Bindable(colour)); } } From 6c0923ec1aa421b3dc4252e8327ae1eb27005b37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 16:54:47 +0900 Subject: [PATCH 200/295] Add argon hold note pieces --- .../Skinning/Argon/ArgonHoldBodyPiece.cs | 90 ++++++++++++++++++ .../Skinning/Argon/ArgonHoldNoteTailPiece.cs | 91 +++++++++++++++++++ .../Argon/ManiaArgonSkinTransformer.cs | 6 ++ 3 files changed, 187 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs new file mode 100644 index 0000000000..10d5a4d267 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + /// + /// Represents length-wise portion of a hold note. + /// + public class ArgonHoldBodyPiece : CompositeDrawable + { + protected readonly Bindable AccentColour = new Bindable(); + protected readonly IBindable IsHitting = new Bindable(); + + private Drawable background = null!; + private Box foreground = null!; + + public ArgonHoldBodyPiece() + { + Blending = BlendingParameters.Additive; + RelativeSizeAxes = Axes.Both; + + // Without this, the width of the body will be slightly larger than the head/tail. + Masking = true; + CornerRadius = ArgonNotePiece.CORNER_RADIUS; + } + + [BackgroundDependencyLoader(true)] + private void load(DrawableHitObject? drawableObject) + { + InternalChildren = new[] + { + background = new Box { RelativeSizeAxes = Axes.Both }, + foreground = new Box + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + }; + + if (drawableObject != null) + { + var holdNote = (DrawableHoldNote)drawableObject; + + AccentColour.BindTo(drawableObject.AccentColour); + IsHitting.BindTo(holdNote.IsHitting); + } + + AccentColour.BindValueChanged(colour => + { + background.Colour = colour.NewValue.Opacity(0.2f); + foreground.Colour = colour.NewValue.Opacity(0.1f); + }, true); + + IsHitting.BindValueChanged(hitting => + { + const float animation_length = 50; + + foreground.ClearTransforms(); + + if (hitting.NewValue) + { + // wait for the next sync point + double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2); + + using (foreground.BeginDelayedSequence(synchronisedOffset)) + { + foreground.FadeTo(1, animation_length).Then() + .FadeTo(0, animation_length) + .Loop(); + } + } + else + { + foreground.FadeOut(animation_length); + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs new file mode 100644 index 0000000000..e1068c6cd8 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldNoteTailPiece.cs @@ -0,0 +1,91 @@ +// 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.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + internal class ArgonHoldNoteTailPiece : CompositeDrawable + { + private readonly IBindable direction = new Bindable(); + private readonly IBindable accentColour = new Bindable(); + + private readonly Box colouredBox; + private readonly Box shadow; + + public ArgonHoldNoteTailPiece() + { + RelativeSizeAxes = Axes.X; + Height = ArgonNotePiece.NOTE_HEIGHT; + + CornerRadius = ArgonNotePiece.CORNER_RADIUS; + Masking = true; + + InternalChildren = new Drawable[] + { + shadow = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Height = 0.82f, + Masking = true, + CornerRadius = ArgonNotePiece.CORNER_RADIUS, + Children = new Drawable[] + { + colouredBox = new Box + { + RelativeSizeAxes = Axes.Both, + } + } + }, + new Circle + { + RelativeSizeAxes = Axes.X, + Height = ArgonNotePiece.CORNER_RADIUS * 2, + }, + }; + } + + [BackgroundDependencyLoader(true)] + private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject) + { + direction.BindTo(scrollingInfo.Direction); + direction.BindValueChanged(onDirectionChanged, true); + + if (drawableObject != null) + { + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(onAccentChanged, true); + } + } + + private void onDirectionChanged(ValueChangedEvent direction) + { + colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up + ? Anchor.TopCentre + : Anchor.BottomCentre; + } + + private void onAccentChanged(ValueChangedEvent accent) + { + colouredBox.Colour = ColourInfo.GradientVertical( + accent.NewValue, + accent.NewValue.Darken(0.1f) + ); + + shadow.Colour = accent.NewValue.Darken(0.5f); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 4843621a1e..c2b258c57c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -28,6 +28,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. switch (maniaComponent.Component) { + case ManiaSkinComponents.HoldNoteBody: + return new ArgonHoldBodyPiece(); + + case ManiaSkinComponents.HoldNoteTail: + return new ArgonHoldNoteTailPiece(); + case ManiaSkinComponents.HoldNoteHead: case ManiaSkinComponents.Note: return new ArgonNotePiece(); From 4cb07b88390fcbba3f4f786b075748dd5aad1700 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 17:46:54 +0900 Subject: [PATCH 201/295] Specify column widths for argon --- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index c2b258c57c..9123fa44d7 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -55,11 +55,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (lookup is ManiaSkinConfigurationLookup maniaLookup) { + int column = maniaLookup.ColumnIndex ?? 0; + var stage = beatmap.GetStageForColumnIndex(column); + switch (maniaLookup.Lookup) { + case LegacyManiaSkinConfigurationLookups.ColumnWidth: + return SkinUtils.As(new Bindable( + stage.IsSpecialColumn(column) ? 80 : 60 + )); + case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: - int column = maniaLookup.ColumnIndex ?? 0; - var stage = beatmap.GetStageForColumnIndex(column); Color4 colour; From a543222a2b2653045882c9b9e0ef58be7e8b723e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 18:03:48 +0900 Subject: [PATCH 202/295] Add ability to pad stage vertically --- .../Argon/ManiaArgonSkinTransformer.cs | 4 +++ osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 2 ++ osu.Game.Rulesets.Mania/UI/Stage.cs | 32 +++++++++++++++++++ .../LegacyManiaSkinConfigurationLookup.cs | 2 ++ 4 files changed, 40 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 9123fa44d7..9ed324793b 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -60,6 +60,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon switch (maniaLookup.Lookup) { + case LegacyManiaSkinConfigurationLookups.StagePaddingBottom: + case LegacyManiaSkinConfigurationLookups.StagePaddingTop: + return SkinUtils.As(new Bindable(30)); + case LegacyManiaSkinConfigurationLookups.ColumnWidth: return SkinUtils.As(new Bindable( stage.IsSpecialColumn(column) ? 80 : 60 diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index 871ec9f1a3..9b3f6d7033 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -36,6 +36,8 @@ namespace osu.Game.Rulesets.Mania.UI AutoSizeAxes = Axes.X; + Masking = true; + InternalChild = columns = new FillFlowContainer { RelativeSizeAxes = Axes.Y, diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index c4cbb74e57..1273cb3d32 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -130,6 +131,37 @@ namespace osu.Game.Rulesets.Mania.UI } } + private ISkinSource currentSkin; + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + currentSkin = skin; + + skin.SourceChanged += onSkinChanged; + onSkinChanged(); + } + + private void onSkinChanged() + { + float paddingTop = currentSkin.GetConfig(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.StagePaddingTop))?.Value ?? 0; + float paddingBottom = currentSkin.GetConfig(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.StagePaddingBottom))?.Value ?? 0; + + Padding = new MarginPadding + { + Top = paddingTop, + Bottom = paddingBottom, + }; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (currentSkin != null) + currentSkin.SourceChanged -= onSkinChanged; + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs index c9e96ab3f1..3ec0ee6006 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs @@ -42,6 +42,8 @@ namespace osu.Game.Skinning HitPosition, ScorePosition, LightPosition, + StagePaddingTop, + StagePaddingBottom, HitTargetImage, ShowJudgementLine, KeyImage, From 59bbbf1c087b54f13d1e72a7c46defc0dde3c8f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 18:07:56 +0900 Subject: [PATCH 203/295] Improve colours and hit metrics on key area --- .../Skinning/Argon/ArgonHitTarget.cs | 3 ++ .../Skinning/Argon/ArgonKeyArea.cs | 30 ++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs index 7d7ef4a15e..9e449623d5 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs @@ -21,6 +21,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon RelativeSizeAxes = Axes.X; Height = ArgonNotePiece.NOTE_HEIGHT; + Masking = true; + CornerRadius = ArgonNotePiece.CORNER_RADIUS; + InternalChildren = new[] { new Box diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 9d16e84f1e..eef7e2ff7c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.UI.Scrolling; using osuTK; using osuTK.Graphics; @@ -53,18 +54,24 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Height = Stage.HIT_TARGET_POSITION, Children = new[] { - background = new Box + new Container { - Name = "Key gradient", + Masking = true, RelativeSizeAxes = Axes.Both, + CornerRadius = ArgonNotePiece.CORNER_RADIUS, + Child = background = new Box + { + Name = "Key gradient", + RelativeSizeAxes = Axes.Both, + }, }, hitTargetLine = new Circle { RelativeSizeAxes = Axes.X, Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, + Origin = Anchor.BottomCentre, Colour = OsuColour.Gray(196 / 255f), - Height = 4, + Height = ArgonNotePiece.CORNER_RADIUS * 2, Masking = true, }, new Container @@ -137,10 +144,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon accentColour = column.AccentColour.GetBoundCopy(); accentColour.BindValueChanged(colour => - { - background.Colour = colour.NewValue.Darken(0.6f); - bottomIcon.Colour = colour.NewValue; - }, true); + { + background.Colour = colour.NewValue.Darken(1f); + bottomIcon.Colour = colour.NewValue; + }, + true); } private void onDirectionChanged(ValueChangedEvent direction) @@ -169,8 +177,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Color4 lightingColour = accentColour.Value.Lighten(0.9f); background - .FadeColour(accentColour.Value.Lighten(0.4f), 40).Then() - .FadeColour(accentColour.Value, 150, Easing.OutQuint); + .FadeColour(accentColour.Value, 40).Then() + .FadeColour(accentColour.Value.Darken(0.4f), 150, Easing.OutQuint); hitTargetLine.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint); hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters @@ -225,7 +233,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { Type = EdgeEffectType.Glow, Colour = lightingColour, - Radius = 30, + Radius = 25, }, lighting_fade_out_duration, Easing.OutQuint); bottomIcon.FadeColour(accentColour.Value, lighting_fade_out_duration, Easing.OutQuint); From f3e3ee81cbda6f9968896c31a172943e01ec2486 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 18:12:55 +0900 Subject: [PATCH 204/295] Add column and stage background to argon skin --- .../Skinning/Argon/ArgonColumnBackground.cs | 101 ++++++++++++++++++ .../Skinning/Argon/ArgonStageBackground.cs | 16 +++ .../Argon/ManiaArgonSkinTransformer.cs | 6 ++ 3 files changed, 123 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonStageBackground.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs new file mode 100644 index 0000000000..c6377f9152 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs @@ -0,0 +1,101 @@ +// 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.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public class ArgonColumnBackground : CompositeDrawable, IKeyBindingHandler + { + private readonly IBindable direction = new Bindable(); + + private Color4 brightColour; + private Color4 dimColour; + + private Box background = null!; + private Box backgroundOverlay = null!; + + [Resolved] + private Column column { get; set; } = null!; + + private Bindable accentColour = null!; + + public ArgonColumnBackground() + { + RelativeSizeAxes = Axes.Both; + + Masking = true; + CornerRadius = ArgonNotePiece.CORNER_RADIUS; + } + + [BackgroundDependencyLoader] + private void load(IScrollingInfo scrollingInfo) + { + InternalChildren = new[] + { + background = new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + }, + backgroundOverlay = new Box + { + Name = "Background Gradient Overlay", + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Blending = BlendingParameters.Additive, + Alpha = 0 + } + }; + + accentColour = column.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => + { + background.Colour = colour.NewValue.Darken(5); + brightColour = colour.NewValue.Opacity(0.6f); + dimColour = colour.NewValue.Opacity(0); + }, true); + + direction.BindTo(scrollingInfo.Direction); + direction.BindValueChanged(onDirectionChanged, true); + } + + private void onDirectionChanged(ValueChangedEvent direction) + { + if (direction.NewValue == ScrollingDirection.Up) + { + backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.TopLeft; + backgroundOverlay.Colour = ColourInfo.GradientVertical(brightColour, dimColour); + } + else + { + backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.BottomLeft; + backgroundOverlay.Colour = ColourInfo.GradientVertical(dimColour, brightColour); + } + } + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == column.Action.Value) + backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint); + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + if (e.Action == column.Action.Value) + backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonStageBackground.cs new file mode 100644 index 0000000000..1881695b14 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonStageBackground.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.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public class ArgonStageBackground : CompositeDrawable + { + public ArgonStageBackground() + { + RelativeSizeAxes = Axes.Both; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 9ed324793b..45ee25e388 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -28,6 +28,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. switch (maniaComponent.Component) { + case ManiaSkinComponents.StageBackground: + return new ArgonStageBackground(); + + case ManiaSkinComponents.ColumnBackground: + return new ArgonColumnBackground(); + case ManiaSkinComponents.HoldNoteBody: return new ArgonHoldBodyPiece(); From 258c93557408a5aefaa8bc473c64cea7e582c532 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 18:29:07 +0900 Subject: [PATCH 205/295] Remove key area background (the column background is enough to work well) --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index eef7e2ff7c..72f5f81890 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -12,7 +12,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.UI.Scrolling; using osuTK; using osuTK.Graphics; @@ -62,6 +61,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Child = background = new Box { Name = "Key gradient", + Alpha = 0, + Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, }, }, @@ -177,8 +178,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Color4 lightingColour = accentColour.Value.Lighten(0.9f); background - .FadeColour(accentColour.Value, 40).Then() - .FadeColour(accentColour.Value.Darken(0.4f), 150, Easing.OutQuint); + .FadeTo(1, 40).Then() + .FadeTo(0.8f, 150, Easing.OutQuint); hitTargetLine.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint); hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters @@ -218,7 +219,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon const double lighting_fade_out_duration = 300; Color4 lightingColour = accentColour.Value.Lighten(0.9f).Opacity(0); - background.FadeColour(accentColour.Value.Darken(0.6f), lighting_fade_out_duration, Easing.OutQuint); + background.FadeTo(0, lighting_fade_out_duration, Easing.OutQuint); topIcon.ScaleTo(1f, 200, Easing.OutQuint); topIcon.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters From b95743092d2e95110f5bf4379c20b29335f569ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 20:51:28 +0900 Subject: [PATCH 206/295] Add argon hit explosion --- .../Skinning/Argon/ArgonHitExplosion.cs | 99 +++++++++++++++++++ .../Argon/ManiaArgonSkinTransformer.cs | 3 + 2 files changed, 102 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs new file mode 100644 index 0000000000..132ef0d528 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs @@ -0,0 +1,99 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public class ArgonHitExplosion : CompositeDrawable, IHitExplosion + { + private const float default_large_faint_size = 0.8f; + + public override bool RemoveWhenNotAlive => true; + + [Resolved] + private Column column { get; set; } = null!; + + private readonly IBindable direction = new Bindable(); + + private Container largeFaint = null!; + + private Bindable accentColour = null!; + + public ArgonHitExplosion() + { + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.X; + Height = ArgonNotePiece.NOTE_HEIGHT; + } + + [BackgroundDependencyLoader] + private void load(IScrollingInfo scrollingInfo) + { + InternalChildren = new Drawable[] + { + largeFaint = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = ArgonNotePiece.CORNER_RADIUS, + Blending = BlendingParameters.Additive, + Child = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, + }, + }; + + direction.BindTo(scrollingInfo.Direction); + direction.BindValueChanged(onDirectionChanged, true); + + accentColour = column.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => + { + largeFaint.Colour = Interpolation.ValueAt(0.8f, colour.NewValue, Color4.White, 0, 1); + + largeFaint.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colour.NewValue, + Roundness = 40, + Radius = 60, + }; + }, true); + } + + private void onDirectionChanged(ValueChangedEvent direction) + { + if (direction.NewValue == ScrollingDirection.Up) + { + Anchor = Anchor.TopCentre; + Y = ArgonNotePiece.NOTE_HEIGHT / 2; + } + else + { + Anchor = Anchor.BottomCentre; + Y = -ArgonNotePiece.NOTE_HEIGHT / 2; + } + } + + public void Animate(JudgementResult result) + { + this.FadeOutFromOne(PoolableHitExplosion.DURATION, Easing.Out); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 45ee25e388..37ab958eee 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -49,6 +49,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case ManiaSkinComponents.KeyArea: return new ArgonKeyArea(); + + case ManiaSkinComponents.HitExplosion: + return new ArgonHitExplosion(); } break; From e4e4c1b66169d842babd595697a70a7db0514f36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 21:39:29 +0900 Subject: [PATCH 207/295] Add very temporary mania judgement display for argon skin --- .../Skinning/Argon/ArgonJudgementPiece.cs | 193 ++++++++++++++++++ .../Argon/ManiaArgonSkinTransformer.cs | 4 + 2 files changed, 197 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs new file mode 100644 index 0000000000..e7dfec256d --- /dev/null +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs @@ -0,0 +1,193 @@ +// 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.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Mania.Skinning.Argon +{ + public class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement + { + protected readonly HitResult Result; + + protected SpriteText JudgementText { get; private set; } = null!; + + private RingExplosion? ringExplosion; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + public ArgonJudgementPiece(HitResult result) + { + Result = result; + Origin = Anchor.Centre; + Y = 160; + } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + JudgementText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = Result.GetDescription().ToUpperInvariant(), + Colour = colours.ForHitResult(Result), + Blending = BlendingParameters.Additive, + Spacing = new Vector2(10, 0), + Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular), + }, + }; + + if (Result.IsHit()) + { + AddInternal(ringExplosion = new RingExplosion(Result) + { + Colour = colours.ForHitResult(Result), + }); + } + } + + /// + /// Plays the default animation for this judgement piece. + /// + /// + /// The base implementation only handles fade (for all result types) and misses. + /// Individual rulesets are recommended to implement their appropriate hit animations. + /// + public virtual void PlayAnimation() + { + switch (Result) + { + default: + JudgementText + .ScaleTo(Vector2.One) + .ScaleTo(new Vector2(1.4f), 1800, Easing.OutQuint); + break; + + case HitResult.Miss: + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); + + this.MoveTo(Vector2.Zero); + this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + + this.RotateTo(0); + this.RotateTo(40, 800, Easing.InQuint); + break; + } + + this.FadeOutFromOne(800); + + ringExplosion?.PlayAnimation(); + } + + public Drawable? GetAboveHitObjectsProxiedContent() => null; + + private class RingExplosion : CompositeDrawable + { + private readonly float travel = 52; + + public RingExplosion(HitResult result) + { + const float thickness = 4; + + const float small_size = 9; + const float large_size = 14; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Blending = BlendingParameters.Additive; + + int countSmall = 0; + int countLarge = 0; + + switch (result) + { + case HitResult.Meh: + countSmall = 3; + travel *= 0.3f; + break; + + case HitResult.Ok: + case HitResult.Good: + countSmall = 4; + travel *= 0.6f; + break; + + case HitResult.Great: + case HitResult.Perfect: + countSmall = 4; + countLarge = 4; + break; + } + + for (int i = 0; i < countSmall; i++) + AddInternal(new RingPiece(thickness) { Size = new Vector2(small_size) }); + + for (int i = 0; i < countLarge; i++) + AddInternal(new RingPiece(thickness) { Size = new Vector2(large_size) }); + } + + public void PlayAnimation() + { + foreach (var c in InternalChildren) + { + const float start_position_ratio = 0.3f; + + float direction = RNG.NextSingle(0, 360); + float distance = RNG.NextSingle(travel / 2, travel); + + c.MoveTo(new Vector2( + MathF.Cos(direction) * distance * start_position_ratio, + MathF.Sin(direction) * distance * start_position_ratio + )); + + c.MoveTo(new Vector2( + MathF.Cos(direction) * distance, + MathF.Sin(direction) * distance + ), 600, Easing.OutQuint); + } + + this.FadeOutFromOne(1000, Easing.OutQuint); + } + + public class RingPiece : CircularContainer + { + public RingPiece(float thickness = 9) + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Masking = true; + BorderThickness = thickness; + BorderColour = Color4.White; + + Child = new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both + }; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 37ab958eee..cf08f7ea3e 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -5,6 +5,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osuTK.Graphics; @@ -24,6 +25,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { switch (component) { + case GameplaySkinComponent resultComponent: + return new ArgonJudgementPiece(resultComponent.Component); + case ManiaSkinComponent maniaComponent: // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. switch (maniaComponent.Component) From 6d14ccb8e0bbeba807255e423f1e9030d6bee39b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 21:46:02 +0900 Subject: [PATCH 208/295] Make special key slightly wider --- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index cf08f7ea3e..93c3a94316 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case LegacyManiaSkinConfigurationLookups.ColumnWidth: return SkinUtils.As(new Bindable( - stage.IsSpecialColumn(column) ? 80 : 60 + stage.IsSpecialColumn(column) ? 120 : 60 )); case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: From e81f550150438741a9da4ee7e26a7899b9d9b673 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 19:35:01 +0900 Subject: [PATCH 209/295] Fix hit explosions not being cleaned up correctly when rewinding --- osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs index 28509d1f4e..a7b94f9f22 100644 --- a/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs @@ -42,6 +42,8 @@ namespace osu.Game.Rulesets.Mania.UI { base.PrepareForUse(); + LifetimeStart = Time.Current; + (skinnableExplosion?.Drawable as IHitExplosion)?.Animate(Result); this.Delay(DURATION).Then().Expire(); From 207c76bdecc83879384a52ee3fcc7d579bc2a7eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Oct 2022 20:17:35 +0900 Subject: [PATCH 210/295] Fix column lighting and key area not handling rewind correctly --- osu.Game.Rulesets.Mania/UI/Column.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 89413f0f1b..61585b2e5b 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Platform; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; @@ -68,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.UI private ISkinSource skin { get; set; } [BackgroundDependencyLoader] - private void load() + private void load(GameHost host) { skin.SourceChanged += onSourceChanged; onSourceChanged(); @@ -82,7 +83,10 @@ namespace osu.Game.Rulesets.Mania.UI Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + // This is pretty dodgy (and will cause weirdness when pausing gameplay) but is better than completely broken rewind. + Clock = host.UpdateThread.Clock, + ProcessCustomClock = false, }; InternalChildren = new[] @@ -94,7 +98,10 @@ namespace osu.Game.Rulesets.Mania.UI HitObjectArea, new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + // This is pretty dodgy (and will cause weirdness when pausing gameplay) but is better than completely broken rewind. + Clock = host.UpdateThread.Clock, + ProcessCustomClock = false, }, background, TopLevelContainer, From 98b578e8e6f37e389dc9ee3bfcc0602e9946cc24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 16:12:10 +0900 Subject: [PATCH 211/295] Remove left-over incorrect fallback logic This was moved to `ManiaClassicSkinTransformer`. --- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 31cfcf9e16..1d39721a2b 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -14,7 +14,6 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; -using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Skinning.Legacy { @@ -151,18 +150,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { if (lookup is ManiaSkinConfigurationLookup maniaLookup) { - var legacyLookup = - base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); - - if (legacyLookup != null) - return legacyLookup; - - // default legacy fallback. - switch (maniaLookup.Lookup) - { - case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour: - return SkinUtils.As(new Bindable(Color4.Black)); - } + return base.GetConfig(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex)); } return base.GetConfig(lookup); From c7405d1c1cc8ddcb0bf0a32799e115992318ae3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 17:00:10 +0900 Subject: [PATCH 212/295] Add missing immediate application of `AccentColour` in `DefaultKeyArea` Co-authored-by: Dan Balasescu --- osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs index 6196850f91..600c9feb73 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components Radius = 5, Colour = colour.NewValue.Opacity(0.5f), }; - }); + }, true); } private void onDirectionChanged(ValueChangedEvent direction) From 4b772643e98999f04ad5b1cf927f5c64c4c84c1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 18:25:07 +0900 Subject: [PATCH 213/295] Use bindable flow for transfer of accent colour from columns to hit objects This fixes the case where changing a skin during gameplay runtime does not correctly convey colour information to off-screen `DrawableHitObject`s. --- osu.Game.Rulesets.Mania/UI/Column.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 89413f0f1b..c6bb3910e2 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -73,13 +73,6 @@ namespace osu.Game.Rulesets.Mania.UI skin.SourceChanged += onSourceChanged; onSourceChanged(); - AccentColour.BindValueChanged(colour => - { - // Manual transfer as hit objects may be moved between column and unbinding is non-trivial. - foreach (var obj in HitObjectContainer.Objects) - obj.AccentColour.Value = colour.NewValue; - }, true); - Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both @@ -142,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.UI DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)drawableHitObject; - maniaObject.AccentColour.Value = AccentColour.Value; + maniaObject.AccentColour.BindTo(AccentColour); maniaObject.CheckHittable = hitPolicy.IsHittable; } From 52ad766f86c933c4621e5fac1dc423227273dded Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 18:25:54 +0900 Subject: [PATCH 214/295] Remove manual binding of nested objects in `DrawableManiaHitObject` This is now handled by `Column` itself. Leaving the logic here as well will cause a circular bindable flow and stack overflow. --- .../Objects/Drawables/DrawableManiaHitObject.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 6cd39d835d..73dc937a00 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -65,22 +65,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Direction.BindValueChanged(OnDirectionChanged, true); } - protected override void OnApply() - { - base.OnApply(); - - if (ParentHitObject != null) - AccentColour.BindTo(ParentHitObject.AccentColour); - } - - protected override void OnFree() - { - base.OnFree(); - - if (ParentHitObject != null) - AccentColour.UnbindFrom(ParentHitObject.AccentColour); - } - protected virtual void OnDirectionChanged(ValueChangedEvent e) { Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; From a8742f0cc31ba72e19e3d82d840cbcaf2acb5b55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 18:46:26 +0900 Subject: [PATCH 215/295] Adjust out-of-the-box background dim back down to 70% It was previously bumped from 60% to 80%, but I've recently felt that this is too high as a default, and takes away from storyboards and video backgrounds. --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e3bfb6b1e9..1378e1691a 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -120,7 +120,7 @@ namespace osu.Game.Configuration // Gameplay SetDefault(OsuSetting.PositionalHitsounds, true); // replaced by level setting below, can be removed 20220703. SetDefault(OsuSetting.PositionalHitsoundsLevel, 0.2f, 0, 1); - SetDefault(OsuSetting.DimLevel, 0.8, 0, 1, 0.01); + SetDefault(OsuSetting.DimLevel, 0.7, 0, 1, 0.01); SetDefault(OsuSetting.BlurLevel, 0, 0, 1, 0.01); SetDefault(OsuSetting.LightenDuringBreaks, true); From 58eaa18725f4535e9b3eb7d785803863b0bac393 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 18:44:16 +0900 Subject: [PATCH 216/295] Add transparency to column backgrounds --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs index c6377f9152..bfa3b34890 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon accentColour = column.AccentColour.GetBoundCopy(); accentColour.BindValueChanged(colour => { - background.Colour = colour.NewValue.Darken(5); + background.Colour = colour.NewValue.Darken(3).Opacity(0.6f); brightColour = colour.NewValue.Opacity(0.6f); dimColour = colour.NewValue.Opacity(0); }, true); From f9b4f491a69fca8e49fa780a1a3da3fbeeec45d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 18:44:24 +0900 Subject: [PATCH 217/295] Increase overall corner radius slightly --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs index f560d2676c..454a6b012b 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { public const float NOTE_HEIGHT = 42; - public const float CORNER_RADIUS = 3; + public const float CORNER_RADIUS = 3.4f; private readonly IBindable direction = new Bindable(); private readonly IBindable accentColour = new Bindable(); From 12e9686092704b5e21f5020396d2d20e9d7162de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 18:44:29 +0900 Subject: [PATCH 218/295] Increase column spacing --- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 93c3a94316..27926c11eb 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -73,6 +73,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon switch (maniaLookup.Lookup) { + case LegacyManiaSkinConfigurationLookups.ColumnSpacing: + return SkinUtils.As(new Bindable(2)); + case LegacyManiaSkinConfigurationLookups.StagePaddingBottom: case LegacyManiaSkinConfigurationLookups.StagePaddingTop: return SkinUtils.As(new Bindable(30)); From e4657a7a3d0784afea6b56fd1f206addf2f9af0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 19:02:08 +0900 Subject: [PATCH 219/295] Fix argon key area lighting being hidden by other columns in one direction --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs | 2 -- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs index 132ef0d528..af179d5580 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { public class ArgonHitExplosion : CompositeDrawable, IHitExplosion { - private const float default_large_faint_size = 0.8f; - public override bool RemoveWhenNotAlive => true; [Resolved] diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 72f5f81890..073b9af307 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -150,6 +150,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon bottomIcon.Colour = colour.NewValue; }, true); + + // Yes, proxy everything. + column.TopLevelContainer.Add(CreateProxy()); } private void onDirectionChanged(ValueChangedEvent direction) From a4f827c7fd22f892612171e42c9dd8f5e06bd223 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 19:02:15 +0900 Subject: [PATCH 220/295] Remove broken `KeyArea` test scene Use `TestSceneStage` instead. --- .../Skinning/TestSceneKeyArea.cs | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs deleted file mode 100644 index bbbd7edb7b..0000000000 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs +++ /dev/null @@ -1,51 +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 osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mania.UI.Components; -using osu.Game.Skinning; -using osuTK; - -namespace osu.Game.Rulesets.Mania.Tests.Skinning -{ - public class TestSceneKeyArea : ManiaSkinnableTestScene - { - [BackgroundDependencyLoader] - private void load() - { - SetContents(_ => new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.8f), - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new ColumnTestContainer(0, ManiaAction.Key1) - { - RelativeSizeAxes = Axes.Both, - Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) - { - RelativeSizeAxes = Axes.Both - }, - }, - new ColumnTestContainer(1, ManiaAction.Key2) - { - RelativeSizeAxes = Axes.Both, - Width = 0.5f, - Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) - { - RelativeSizeAxes = Axes.Both - }, - }, - } - }); - } - } -} From 551b7f0d1c0e43ecf1e50dd6c66e219c40953a11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 19:08:55 +0900 Subject: [PATCH 221/295] Fix corner radius visible below hit target line --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 073b9af307..dd6aeadd65 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -50,7 +50,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon InternalChild = directionContainer = new Container { RelativeSizeAxes = Axes.X, - Height = Stage.HIT_TARGET_POSITION, + // Ensure the area is tall enough to put the target line in the correct location. + // This is to also allow the main background component to overlap the target line + // and avoid an inner corner radius being shown below the target line. + Height = Stage.HIT_TARGET_POSITION + ArgonNotePiece.CORNER_RADIUS * 2, Children = new[] { new Container @@ -70,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { RelativeSizeAxes = Axes.X, Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, + Origin = Anchor.TopCentre, Colour = OsuColour.Gray(196 / 255f), Height = ArgonNotePiece.CORNER_RADIUS * 2, Masking = true, From 08c3f3ae6df6387210d8255dff878256293f8699 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 19:27:00 +0900 Subject: [PATCH 222/295] Adjust lighting and timing of key area in general --- .../Skinning/Argon/ArgonKeyArea.cs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index dd6aeadd65..badca71f36 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -65,7 +66,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { Name = "Key gradient", Alpha = 0, - Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, }, }, @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon accentColour = column.AccentColour.GetBoundCopy(); accentColour.BindValueChanged(colour => { - background.Colour = colour.NewValue.Darken(1f); + background.Colour = colour.NewValue.Darken(0.2f); bottomIcon.Colour = colour.NewValue; }, true); @@ -180,18 +180,19 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (e.Action != column.Action.Value) return false; - const double lighting_fade_in_duration = 50; - Color4 lightingColour = accentColour.Value.Lighten(0.9f); + const double lighting_fade_in_duration = 70; + Color4 lightingColour = getLightingColour(); background - .FadeTo(1, 40).Then() - .FadeTo(0.8f, 150, Easing.OutQuint); + .FadeTo(1, lighting_fade_in_duration, Easing.OutQuint) + .Then() + .FadeTo(0.6f, 500, Easing.In); hitTargetLine.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint); hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = lightingColour.Opacity(0.7f), + Colour = lightingColour.Opacity(0.4f), Radius = 20, }, lighting_fade_in_duration, Easing.OutQuint); @@ -210,7 +211,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = lightingColour.Opacity(0.3f), + Colour = lightingColour.Opacity(0.2f), Radius = 60, }, lighting_fade_in_duration, Easing.OutQuint); } @@ -222,10 +223,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { if (e.Action != column.Action.Value) return; - const double lighting_fade_out_duration = 300; - Color4 lightingColour = accentColour.Value.Lighten(0.9f).Opacity(0); + const double lighting_fade_out_duration = 800; - background.FadeTo(0, lighting_fade_out_duration, Easing.OutQuint); + Color4 lightingColour = getLightingColour().Opacity(0); + + // background fades out faster than lighting elements to give better definition to the player. + background.FadeTo(0.3f, 50, Easing.OutQuint) + .Then() + .FadeOut(lighting_fade_out_duration, Easing.OutQuint); topIcon.ScaleTo(1f, 200, Easing.OutQuint); topIcon.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters @@ -255,5 +260,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon }, lighting_fade_out_duration, Easing.OutQuint); } } + + private Color4 getLightingColour() => Interpolation.ValueAt(0.2f, accentColour.Value, Color4.White, 0, 1); } } From c518fbf44a2bb03719fd2331c06bc3ca8cc52f76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 19:31:23 +0900 Subject: [PATCH 223/295] Adjust argon hold bodies to better define away from the stage background --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs index 10d5a4d267..7fbaf8ba64 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon AccentColour.BindValueChanged(colour => { - background.Colour = colour.NewValue.Opacity(0.2f); + background.Colour = colour.NewValue.Darken(1.5f); foreground.Colour = colour.NewValue.Opacity(0.1f); }, true); From 8101eea50dedc0a23bdd8bf63b12343be0bab97f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 20:04:39 +0900 Subject: [PATCH 224/295] Fix first glow of key area elements not working due to missing type --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index badca71f36..98e46c1eac 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -77,6 +77,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Colour = OsuColour.Gray(196 / 255f), Height = ArgonNotePiece.CORNER_RADIUS * 2, Masking = true, + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow }, }, new Container { @@ -100,6 +101,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Size = new Vector2(icon_circle_size), Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow }, }, new Circle { @@ -108,6 +110,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Size = new Vector2(icon_circle_size), Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow }, }, new Circle { @@ -116,6 +119,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Size = new Vector2(icon_circle_size), Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow }, }, } }, @@ -128,6 +132,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Masking = true, BorderThickness = 4, BorderColour = Color4.White, + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow }, Children = new Drawable[] { new Box From d5490523ea8dd4a8f6d16541ff9ef61bc6d75efb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 20:05:08 +0900 Subject: [PATCH 225/295] Adjust hold notes further --- .../Skinning/Argon/ArgonHoldBodyPiece.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs index 7fbaf8ba64..c4b2181879 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Skinning.Default; using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; @@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon /// /// Represents length-wise portion of a hold note. /// - public class ArgonHoldBodyPiece : CompositeDrawable + public class ArgonHoldBodyPiece : CompositeDrawable, IHoldNoteBody { protected readonly Bindable AccentColour = new Bindable(); protected readonly IBindable IsHitting = new Bindable(); @@ -26,7 +27,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon public ArgonHoldBodyPiece() { - Blending = BlendingParameters.Additive; RelativeSizeAxes = Axes.Both; // Without this, the width of the body will be slightly larger than the head/tail. @@ -52,14 +52,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { var holdNote = (DrawableHoldNote)drawableObject; - AccentColour.BindTo(drawableObject.AccentColour); + AccentColour.BindTo(holdNote.AccentColour); IsHitting.BindTo(holdNote.IsHitting); } AccentColour.BindValueChanged(colour => { background.Colour = colour.NewValue.Darken(1.5f); - foreground.Colour = colour.NewValue.Opacity(0.1f); + foreground.Colour = colour.NewValue.Opacity(0.2f); }, true); IsHitting.BindValueChanged(hitting => @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon using (foreground.BeginDelayedSequence(synchronisedOffset)) { foreground.FadeTo(1, animation_length).Then() - .FadeTo(0, animation_length) + .FadeTo(0.5f, animation_length) .Loop(); } } @@ -86,5 +86,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon } }); } + + public void Recycle() + { + foreground.ClearTransforms(); + foreground.Alpha = 0; + } } } From 589b764b77803f65f5807b1cabd4efefd73a9de6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 20:30:38 +0900 Subject: [PATCH 226/295] Final tweaks because I don't know when to stop --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs index 98e46c1eac..7670c9bdf2 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs @@ -189,9 +189,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Color4 lightingColour = getLightingColour(); background + .FlashColour(accentColour.Value.Lighten(0.8f), 200, Easing.OutQuint) .FadeTo(1, lighting_fade_in_duration, Easing.OutQuint) .Then() - .FadeTo(0.6f, 500, Easing.In); + .FadeTo(0.8f, 500); hitTargetLine.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint); hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters From 588713497ddab075e99ae4dd123d0a7c82a4bce8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 20:43:26 +0900 Subject: [PATCH 227/295] Attempt to make hold notes more legible at low background dims --- .../Skinning/Argon/ArgonColumnBackground.cs | 2 +- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs index bfa3b34890..598a765d3c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonColumnBackground.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon accentColour = column.AccentColour.GetBoundCopy(); accentColour.BindValueChanged(colour => { - background.Colour = colour.NewValue.Darken(3).Opacity(0.6f); + background.Colour = colour.NewValue.Darken(3).Opacity(0.8f); brightColour = colour.NewValue.Opacity(0.6f); dimColour = colour.NewValue.Opacity(0); }, true); diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs index c4b2181879..757190c4ae 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs @@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon // Without this, the width of the body will be slightly larger than the head/tail. Masking = true; CornerRadius = ArgonNotePiece.CORNER_RADIUS; + Blending = BlendingParameters.Additive; } [BackgroundDependencyLoader(true)] @@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon AccentColour.BindValueChanged(colour => { - background.Colour = colour.NewValue.Darken(1.5f); + background.Colour = colour.NewValue.Darken(1.2f); foreground.Colour = colour.NewValue.Opacity(0.2f); }, true); From e1e12f41e94c2fe7909048867ca217a414bbae51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 21:00:02 +0900 Subject: [PATCH 228/295] Add more comprehensive inline comment --- osu.Game.Rulesets.Mania/UI/Column.cs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index be26c41ceb..3d46bdaa7b 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -71,15 +71,14 @@ namespace osu.Game.Rulesets.Mania.UI [BackgroundDependencyLoader] private void load(GameHost host) { + SkinnableDrawable keyArea; + skin.SourceChanged += onSourceChanged; onSourceChanged(); Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) { RelativeSizeAxes = Axes.Both, - // This is pretty dodgy (and will cause weirdness when pausing gameplay) but is better than completely broken rewind. - Clock = host.UpdateThread.Clock, - ProcessCustomClock = false, }; InternalChildren = new[] @@ -89,18 +88,18 @@ namespace osu.Game.Rulesets.Mania.UI // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements background.CreateProxy(), HitObjectArea, - new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) + keyArea = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) { RelativeSizeAxes = Axes.Both, - // This is pretty dodgy (and will cause weirdness when pausing gameplay) but is better than completely broken rewind. - Clock = host.UpdateThread.Clock, - ProcessCustomClock = false, }, background, TopLevelContainer, new ColumnTouchInputArea(this) }; + applyGameWideClock(background); + applyGameWideClock(keyArea); + TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); RegisterPool(10, 50); @@ -108,6 +107,18 @@ namespace osu.Game.Rulesets.Mania.UI RegisterPool(10, 50); RegisterPool(10, 50); RegisterPool(50, 250); + + // Some elements don't handle rewind correctly and fixing them is non-trivial. + // In the future we need a better solution to this, but as a temporary work-around, give these components the game-wide + // clock so they don't need to worry about rewind. + // This only works because they handle OnPressed/OnReleased which results in a correct state while rewinding. + // + // This is kinda dodgy (and will cause weirdness when pausing gameplay) but is better than completely broken rewind. + void applyGameWideClock(Drawable drawable) + { + drawable.Clock = host.UpdateThread.Clock; + drawable.ProcessCustomClock = false; + } } private void onSourceChanged() From 072b64b8d4d7cc68002e4caaafba18512ae19a65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 21:16:11 +0900 Subject: [PATCH 229/295] Update all dependencies --- ...su.Game.Rulesets.EmptyFreeform.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 4 ++-- ...u.Game.Rulesets.EmptyScrolling.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 4 ++-- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 4 ++-- .../osu.Game.Rulesets.Catch.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Mania.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Osu.Tests.csproj | 4 ++-- .../osu.Game.Rulesets.Taiko.Tests.csproj | 4 ++-- osu.Game.Tests/osu.Game.Tests.csproj | 6 +++--- .../osu.Game.Tournament.Tests.csproj | 4 ++-- osu.Game/osu.Game.csproj | 18 +++++++++--------- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index b8604169aa..936808f38b 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -9,9 +9,9 @@ false - + - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 4117452579..35e7742172 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -9,9 +9,9 @@ false - + - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index 0b119c8680..c1044965b5 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -9,9 +9,9 @@ false - + - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 4117452579..35e7742172 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -9,9 +9,9 @@ false - + - + diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index 36ffd3b5b6..d62d422f33 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -7,9 +7,9 @@ - + - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index d45f8a9692..c9db824615 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -1,9 +1,9 @@  - + - + WinExe diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 2951076591..0d7b03d830 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -1,9 +1,9 @@  - + - + WinExe diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index c2973644cf..1eb1c85d93 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -1,10 +1,10 @@  - + - + WinExe diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index e8eaff4368..38e61f5624 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -1,9 +1,9 @@  - + - + WinExe diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 2572864e09..bdf8cc5136 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,11 +1,11 @@  - - + + - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 5512b26863..bdef46a6b2 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -4,9 +4,9 @@ osu.Game.Tournament.Tests.TournamentTestRunner - + - + WinExe diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 26738673c8..efdb6c6995 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,15 +18,15 @@ - + - + - - - - + + + + @@ -34,13 +34,13 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + - + From cac5e734951d96101b931eb044bcc6811783a1c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 21:28:25 +0900 Subject: [PATCH 230/295] Update props references --- osu.Android.props | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c0be002bb5..2a678f1c61 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -56,6 +56,6 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index af1e21edab..47872e4ff7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -83,10 +83,10 @@ - + - + From cff38e532a6be67e444b12df77dbfd5faabc90a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Oct 2022 22:56:38 +0900 Subject: [PATCH 231/295] Attempt to remove console output --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33ec3d6602..56b3ebe87b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: run: dotnet build -c Debug -warnaserror osu.Desktop.slnf - name: Test - run: dotnet test $pwd/**/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx" + run: dotnet test $pwd/**/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx" -- NUnit.ConsoleOut=0 shell: pwsh # Attempt to upload results even if test fails. From 903c8612c89565f7ba25c337304abef567ed75db Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Fri, 7 Oct 2022 20:34:31 +0200 Subject: [PATCH 232/295] Move endTime to next timing point --- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index dec81d9bbd..2a2aaaecba 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Objects int currentBeat = 0; // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object - double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; + double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastHitTime + currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; double barLength = currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; From 63ffaa69292afc14bfd9c8b4174b11b39cd620de Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Fri, 7 Oct 2022 20:38:32 +0200 Subject: [PATCH 233/295] Respect OmitFirstBarLine effect --- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index 2a2aaaecba..85bff630e3 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -33,6 +31,7 @@ namespace osu.Game.Rulesets.Objects double lastHitTime = 1 + lastObject.GetEndTime(); var timingPoints = beatmap.ControlPointInfo.TimingPoints; + var effectPoints = beatmap.ControlPointInfo.EffectPoints; if (timingPoints.Count == 0) return; @@ -40,14 +39,22 @@ namespace osu.Game.Rulesets.Objects for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint currentTimingPoint = timingPoints[i]; + EffectControlPoint? currentEffectPoint = effectPoints.FirstOrDefault(p => p.Time == currentTimingPoint.Time); int currentBeat = 0; // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastHitTime + currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; + double startTime = currentTimingPoint.Time; + double barLength = currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; - for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) + if (currentEffectPoint != null && currentEffectPoint.OmitFirstBarLine) + { + startTime += barLength; + } + + for (double t = startTime; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) { double roundedTime = Math.Round(t, MidpointRounding.AwayFromZero); From f889f0df364614214445733214d68ad94bc94416 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Fri, 7 Oct 2022 20:41:45 +0200 Subject: [PATCH 234/295] Add tests for the BarLineGenerator --- .../TestSceneBarLineGeneration.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs new file mode 100644 index 0000000000..4bff9c9646 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneBarLineGeneration : OsuTestScene + { + [Test] + public void TestCloseBarLineGeneration() + { + const double start_time = 1000; + + var beatmap = new Beatmap + { + HitObjects = + { + new Hit + { + Type = HitType.Centre, + StartTime = start_time + } + }, + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty { SliderTickRate = 4 }, + Ruleset = new TaikoRuleset().RulesetInfo + }, + }; + + beatmap.ControlPointInfo.Add(start_time, new TimingControlPoint()); + beatmap.ControlPointInfo.Add(start_time + 1, new TimingControlPoint()); + + var barlines = new BarLineGenerator(beatmap).BarLines; + + AddAssert("first barline generated", () => barlines.Any(b => b.StartTime == start_time)); + AddAssert("second barline generated", () => barlines.Any(b => b.StartTime == start_time + 1)); + } + + [Test] + public void TestOmitBarLineEffectPoint() + { + const double start_time = 1000; + const double beat_length = 500; + + const int time_signature_numerator = 4; + + var beatmap = new Beatmap + { + HitObjects = + { + new Hit + { + Type = HitType.Centre, + StartTime = start_time + } + }, + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty { SliderTickRate = 4 }, + Ruleset = new TaikoRuleset().RulesetInfo + }, + }; + + beatmap.ControlPointInfo.Add(start_time, new TimingControlPoint + { + BeatLength = beat_length, + TimeSignature = new TimeSignature(time_signature_numerator) + }); + + beatmap.ControlPointInfo.Add(start_time, new EffectControlPoint { OmitFirstBarLine = true }); + + var barlines = new BarLineGenerator(beatmap).BarLines; + + AddAssert("first barline ommited", () => !barlines.Any(b => b.StartTime == start_time)); + AddAssert("second barline generated", () => barlines.Any(b => b.StartTime == start_time + (beat_length * time_signature_numerator))); + } + } +} From 9d45f1ec12747df117d44b3a2b870ed20f709dc3 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Fri, 7 Oct 2022 22:08:12 +0200 Subject: [PATCH 235/295] Fix code style --- osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs index 4bff9c9646..095fddc33f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineGeneration.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Taiko.Tests }, }; - beatmap.ControlPointInfo.Add(start_time, new TimingControlPoint + beatmap.ControlPointInfo.Add(start_time, new TimingControlPoint { BeatLength = beat_length, TimeSignature = new TimeSignature(time_signature_numerator) @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Taiko.Tests var barlines = new BarLineGenerator(beatmap).BarLines; - AddAssert("first barline ommited", () => !barlines.Any(b => b.StartTime == start_time)); + AddAssert("first barline ommited", () => barlines.All(b => b.StartTime != start_time)); AddAssert("second barline generated", () => barlines.Any(b => b.StartTime == start_time + (beat_length * time_signature_numerator))); } } From ff14453c2ba7e289e518586b2b97092d4355f761 Mon Sep 17 00:00:00 2001 From: pfg Date: Sat, 8 Oct 2022 12:42:29 -0400 Subject: [PATCH 236/295] Preserve collections when saving a beatmap --- osu.Game/Beatmaps/BeatmapManager.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2c6edb64f8..1c9933387c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -17,6 +17,7 @@ using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps.Formats; +using osu.Game.Collections; using osu.Game.Database; using osu.Game.Extensions; using osu.Game.IO.Archives; @@ -311,6 +312,8 @@ namespace osu.Game.Beatmaps if (existingFileInfo != null) DeleteFile(setInfo, existingFileInfo); + string? oldMd5Hash = beatmapInfo.MD5Hash; + beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); beatmapInfo.Hash = stream.ComputeSHA2Hash(); @@ -327,6 +330,12 @@ namespace osu.Game.Beatmaps setInfo.CopyChangesToRealm(liveBeatmapSet); + foreach (var collection in r.All()) + { + if (collection.BeatmapMD5Hashes.Remove(oldMd5Hash)) + collection.BeatmapMD5Hashes.Add(beatmapInfo.MD5Hash); + } + ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); } From edec61372450df63efb434a40b90329c1dfb26f4 Mon Sep 17 00:00:00 2001 From: pfg Date: Sat, 8 Oct 2022 13:58:42 -0400 Subject: [PATCH 237/295] Remove unnecessary '?' --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 1c9933387c..71cafd46c0 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -312,7 +312,7 @@ namespace osu.Game.Beatmaps if (existingFileInfo != null) DeleteFile(setInfo, existingFileInfo); - string? oldMd5Hash = beatmapInfo.MD5Hash; + string oldMd5Hash = beatmapInfo.MD5Hash; beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); beatmapInfo.Hash = stream.ComputeSHA2Hash(); From 098a56a78494613413471ecdde26aef5f827ee48 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 Oct 2022 21:24:57 +0300 Subject: [PATCH 238/295] Add custom container extension support --- .../Containers/Markdown/OsuMarkdownContainer.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index 62fefe201d..3855ed6d4e 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -5,6 +5,7 @@ using Markdig; using Markdig.Extensions.AutoLinks; +using Markdig.Extensions.CustomContainers; using Markdig.Extensions.EmphasisExtras; using Markdig.Extensions.Footnotes; using Markdig.Extensions.Tables; @@ -32,6 +33,12 @@ namespace osu.Game.Graphics.Containers.Markdown /// protected virtual bool Autolinks => false; + /// + /// Allows this markdown container to parse custom containers (used for flags and infoboxes). + /// + /// + protected virtual bool CustomContainers => false; + public OsuMarkdownContainer() { LineSpacing = 21; @@ -107,6 +114,9 @@ namespace osu.Game.Graphics.Containers.Markdown if (Autolinks) pipeline = pipeline.UseAutoLinks(); + if (CustomContainers) + pipeline.UseCustomContainers(); + return pipeline.Build(); } } From a9aba743514d13de403b6685d1475afa914beb63 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 Oct 2022 21:26:20 +0300 Subject: [PATCH 239/295] Add markdown country flag support --- .../Markdown/OsuMarkdownTextFlowContainer.cs | 27 +++++++++++++++++++ .../Wiki/Markdown/WikiMarkdownContainer.cs | 7 +++++ 2 files changed, 34 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index db8abfb269..fb8f13ab84 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -3,6 +3,9 @@ #nullable disable +using System; +using System.Linq; +using Markdig.Extensions.CustomContainers; using Markdig.Syntax.Inlines; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -11,6 +14,9 @@ using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Overlays; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; namespace osu.Game.Graphics.Containers.Markdown { @@ -33,6 +39,27 @@ namespace osu.Game.Graphics.Containers.Markdown protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic) => CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic)); + protected override void AddCustomComponent(CustomContainerInline inline) + { + if (!(inline.FirstChild is LiteralInline literal)) + { + base.AddCustomComponent(inline); + return; + } + + string[] attributes = literal.Content.ToString().Trim(' ', '{', '}').Split(); + string flagAttribute = attributes.SingleOrDefault(a => a.StartsWith(@"flag", StringComparison.Ordinal)); + + if (flagAttribute == null) + { + base.AddCustomComponent(inline); + return; + } + + string flag = flagAttribute.Split('=').Last().Trim('"'); + AddDrawable(new DrawableFlag(Enum.Parse(flag)) { Size = new Vector2(20, 15) }); + } + private class OsuMarkdownInlineCode : Container { [Resolved] diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 4f1083a75c..15c455416c 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -4,6 +4,7 @@ #nullable disable using System.Linq; +using Markdig.Extensions.CustomContainers; using Markdig.Extensions.Yaml; using Markdig.Syntax; using Markdig.Syntax.Inlines; @@ -16,6 +17,7 @@ namespace osu.Game.Overlays.Wiki.Markdown public class WikiMarkdownContainer : OsuMarkdownContainer { protected override bool Footnotes => true; + protected override bool CustomContainers => true; public string CurrentPath { @@ -26,6 +28,11 @@ namespace osu.Game.Overlays.Wiki.Markdown { switch (markdownObject) { + case CustomContainer: + // infoboxes are parsed into CustomContainer objects, but we don't have support for infoboxes yet. + // todo: add support for infobox. + break; + case YamlFrontMatterBlock yamlFrontMatterBlock: container.Add(new WikiNoticeContainer(yamlFrontMatterBlock)); break; From 13083813bdbdaf6c39520fb3e5467c3f10e9e8b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 Oct 2022 21:26:42 +0300 Subject: [PATCH 240/295] Update hardcoded article page --- osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 558bff2f3c..27cd74bb1f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.Online Locale = "en", Subtitle = "Article styling criteria", Markdown = - "# Formatting\n\n*For the writing standards, see: [Article style criteria/Writing](../Writing)*\n\n*Notice: This article uses [RFC 2119](https://tools.ietf.org/html/rfc2119 \"IETF Tools\") to describe requirement levels.*\n\n## Locales\n\nListed below are the properly-supported locales for the wiki:\n\n| File Name | Locale Name | Native Script |\n| :-- | :-- | :-- |\n| `en.md` | English | English |\n| `ar.md` | Arabic | اَلْعَرَبِيَّةُ |\n| `be.md` | Belarusian | Беларуская мова |\n| `bg.md` | Bulgarian | Български |\n| `cs.md` | Czech | Česky |\n| `da.md` | Danish | Dansk |\n| `de.md` | German | Deutsch |\n| `gr.md` | Greek | Ελληνικά |\n| `es.md` | Spanish | Español |\n| `fi.md` | Finnish | Suomi |\n| `fr.md` | French | Français |\n| `hu.md` | Hungarian | Magyar |\n| `id.md` | Indonesian | Bahasa Indonesia |\n| `it.md` | Italian | Italiano |\n| `ja.md` | Japanese | 日本語 |\n| `ko.md` | Korean | 한국어 |\n| `nl.md` | Dutch | Nederlands |\n| `no.md` | Norwegian | Norsk |\n| `pl.md` | Polish | Polski |\n| `pt.md` | Portuguese | Português |\n| `pt-br.md` | Brazilian Portuguese | Português (Brasil) |\n| `ro.md` | Romanian | Română |\n| `ru.md` | Russian | Русский |\n| `sk.md` | Slovak | Slovenčina |\n| `sv.md` | Swedish | Svenska |\n| `th.md` | Thai | ไทย |\n| `tr.md` | Turkish | Türkçe |\n| `uk.md` | Ukrainian | Українська мова |\n| `vi.md` | Vietnamese | Tiếng Việt |\n| `zh.md` | Chinese (Simplified) | 简体中文 |\n| `zh-tw.md` | Traditional Chinese (Taiwan) | 繁體中文(台灣) |\n\n*Note: The website will give readers their selected language's version of an article. If it is not available, the English version will be given.*\n\n### Content parity\n\nTranslations are subject to strict content parity with their English article, in the sense that they must have the same message, regardless of grammar and syntax. Any changes to the translations' meanings must be accompanied by equivalent changes to the English article.\n\nThere are some cases where the content is allowed to differ:\n\n- Articles originally written in a language other than English (in this case, English should act as the translation)\n- Explanations of English words that are common terms in the osu! community\n- External links\n- Tags\n- Subcommunity-specific explanations\n\n## Front matter\n\nFront matter must be placed at the very top of the file. It is written in [YAML](https://en.wikipedia.org/wiki/YAML#Example \"YAML Wikipedia article\") and describes additional information about the article. This must be surrounded by three hyphens (`---`) on the lines above and below it, and an empty line must follow it before the title heading.\n\n### Articles that need help\n\n*Note: Avoid translating English articles with this tag. In addition to this, this tag should be added when the translation needs its own clean up.*\n\nThe `needs_cleanup` tag may be added to articles that need rewriting or formatting help. It is also acceptable to open an issue on GitHub for this purpose. This tag must be written as shown below:\n\n```yaml\nneeds_cleanup: true\n```\n\nWhen adding this tag to an article, [comments](#comments) should also be added to explain what needs to be done to remove the tag.\n\n### Outdated articles\n\n*Note: Avoid translating English articles with this tag. If the English article has this tag, the translation must also have this tag.*\n\nTranslated articles that are outdated must use the `outdated` tag when the English variant is updated. English articles may also become outdated when the content they contain is misleading or no longer relevant. This tag must be written as shown below:\n\n```yaml\noutdated: true\n```\n\nWhen adding this tag to an article, [comments](#comments) should also be added to explain what needs to be updated to remove the tag.\n\n### Tagging articles\n\nTags help the website's search engine query articles better. Tags should be written in the same language as the article and include the original list of tags. Tags should use lowercase letters where applicable.\n\nFor example, an article called \"Beatmap discussion\" may include the following tags:\n\n```yaml\ntags:\n - beatmap discussions\n - modding V2\n - MV2\n```\n\n### Translations without reviews\n\n*Note: Wiki maintainers will determine and apply this mark prior to merging.*\n\nSometimes, translations are added to the wiki without review from other native speakers of the language. In this case, the `no_native_review` mark is added to let future translators know that it may need to be checked again. This tag must be written as shown below:\n\n```yaml\nno_native_review: true\n```\n\n## Article naming\n\n*See also: [Folder names](#folder-names) and [Titles](#titles)*\n\nArticle titles should be singular and use sentence case. See [Wikipedia's naming conventions article](https://en.wikipedia.org/wiki/Wikipedia:Naming_conventions_(plurals) \"Wikipedia\") for more details.\n\nArticle titles should match the folder name it is in (spaces may replace underscores (`_`) where appropriate). If the folder name changes, the article title should be changed to match it and vice versa.\n\n---\n\nContest and tournament articles are an exception. The folder name must use abbreviations, acronyms, or initialisms. The article's title must be the full name of the contest or tournament.\n\n## Folder and file structure\n\n### Folder names\n\n*See also: [Article naming](#article-naming)*\n\nFolder names must be in English and use sentence case.\n\nFolder names must only use these characters:\n\n- uppercase and lowercase letters\n- numbers\n- underscores (`_`)\n- hyphens (`-`)\n- exclamation marks (`!`)\n\n### Article file names\n\nThe file name of an article can be found in the `File Name` column of the [locales section](#locales). The location of a translated article must be placed in the same folder as the English article.\n\n### Index articles\n\nAn index article must be created if the folder is intended to only hold other articles. Index articles must contain a list of articles that are inside its own folder. They may also contain other information, such as a lead paragraph or descriptions of the linked articles.\n\n### Disambiguation articles\n\n[Disambiguation](/wiki/Disambiguation) articles must be placed in the `/wiki/Disambiguation` folder. The main page must be updated to include the disambiguation article. Refer to [Disambiguation/Mod](/wiki/Disambiguation/Mod) as an example.\n\nRedirects must be updated to have the ambiguous keyword(s) redirect to the disambiguation article.\n\nArticles linked from a disambiguation article must have a [For other uses](#for-other-uses) hatnote.\n\n## HTML\n\nHTML must not be used, with exception for [comments](#comments). The structure of the article must be redone if HTML is used.\n\n### Comments\n\nHTML comments should be used for marking to-dos, but may also be used to annotate text. They should be on their own line, but can be placed inline in a paragraph. If placed inline, the start of the comment must not have a space.\n\nBad example:\n\n```markdown\nHTML comments should be used for marking to-dos or annotate text.\n```\n\nGood example:\n\n```markdown\nHTML comments should be used for marking to-dos or annotate text.\n```\n\n## Editing\n\n### End of line sequence\n\n*Caution: Uploading Markdown files using `CRLF` (carriage return and line feed) via GitHub will result in those files using `CRLF`. To prevent this, set the line ending to `LF` (line feed) before uploading.*\n\nMarkdown files must be checked in using the `LF` end of line sequence.\n\n### Escaping\n\nMarkdown syntax should be escaped as needed. However, article titles are parsed as plain text and so must not be escaped.\n\n### Paragraphs\n\nEach paragraph must be followed by one empty line.\n\n### Line breaks\n\nLine breaks must use a backslash (`\\`).\n\nLine breaks must be used sparingly.\n\n## Hatnote\n\n*Not to be confused with [Notice](#notice).*\n\nHatnotes are short notes placed at the top of an article or section to help readers navigate to related articles or inform them about related topics.\n\nHatnotes must be italicised and be placed immediately after the heading. If multiple hatnotes are used, they must be on the same paragraph separated with a line break.\n\n### Main page\n\n*Main page* hatnotes direct the reader to the main article of a topic. When this hatnote is used, it implies that the section it is on is a summary of what the linked page is about. This hatnote should have only one link. These must be formatted as follows:\n\n```markdown\n*Main page: {article}*\n\n*Main pages: {article} and {article}*\n```\n\n### See also\n\n*See also* hatnotes suggest to readers other points of interest from a given article or section. These must be formatted as follows:\n\n```markdown\n*See also: {article}*\n\n*See also: {article} and {article}*\n```\n\n### For see\n\n*For see* hatnotes are similar to *see also* hatnotes, but are generally more descriptive and direct. This hatnote may use more than one link if necessary. These must be formatted as follows:\n\n```markdown\n*For {description}, see: {article}`*\n\n*For {description}, see: {article} and {article}`*\n```\n\n### Not to be confused with\n\n*Not to be confused with* hatnotes help distinguish ambiguous or misunderstood article titles or sections. This hatnote may use more than one link if necessary. These must be formatted as follows:\n\n```markdown\n*Not to be confused with {article}.*\n\n*Not to be confused with {article} or {article}.*\n```\n\n### For other uses\n\n*For other uses* hatnotes are similar to *not to be confused with* hatnotes, but links directly to the [disambiguation article](#disambiguation-articles). This hatnote must only link to the disambiguation article. These must be formatted as follows:\n\n```markdown\n*For other uses, see {disambiguation article}.*\n```\n\n## Notice\n\n*Not to be confused with [Hatnote](#hatnote).*\n\nA notice should be placed where appropriate in a section, but must start off the paragraph and use italics. Notices may contain bolding where appropriate, but should be kept to a minimum. Notices must be written as complete sentences. Thus, unlike most [hatnotes](#hatnotes), must use a full stop (`.`) or an exclamation mark (`!`) if appropriate. Anything within the same paragraph of a notice must also be italicised. These must be formatted as follows:\n\n```markdown\n*Note: {note}.*\n\n*Notice: {notice}.*\n\n*Caution: {caution}.*\n\n*Warning: {warning}.*\n```\n\n- `Note` should be used for factual or trivial details.\n- `Notice` should be used for reminders or to draw attention to something that the reader should be made aware of.\n- `Caution` should be used to warn the reader to avoid unintended consequences.\n- `Warning` should be used to warn the reader that action may be taken against them.\n\n## Emphasising\n\n### Bold\n\nBold must use double asterisks (`**`).\n\nLead paragraphs may bold the first occurrence of the article's title.\n\n### Italics\n\nItalics must use single asterisks (`*`).\n\nNames of work or video games should be italicised. osu!—the game—is exempt from this.\n\nThe first occurrence of an abbreviation, acronym, or initialism may be italicised.\n\nItalics may also be used to provide emphasis or help with readability.\n\n## Headings\n\nAll headings must use sentence case.\n\nHeadings must use the [ATX (hash) style](https://github.github.com/gfm/#atx-headings \"GitHub\") and must have an empty line before and after the heading. The title heading is an exception when it is on the first line. If this is the case, there only needs to be an empty line after the title heading.\n\nHeadings must not exceed a heading level of 5 and must not be used to style or format text.\n\n### Titles\n\n*See also: [Article naming](#article-naming)*\n\n*Caution: Titles are parsed as plain text; they must not be escaped.*\n\nThe first heading in all articles must be a level 1 heading, being the article's title. All headings afterwards must be [section headings](#sections). Titles must not contain formatting, links, or images.\n\nThe title heading must be on the first line, unless [front matter](#front-matter) is being used. If that is the case, the title heading must go after it and have an empty line before the title heading.\n\n### Sections\n\nSection headings must use levels 2 to 5. The section heading proceeding the [title heading](#titles) must be a level 2 heading. Unlike titles, section headings may have small image icons.\n\nSection headings must not skip a heading level (i.e. do not go from a level 2 heading to a level 4 heading) and must not contain formatting or links.\n\n*Notice: On the website, heading levels 4 and 5 will not appear in the table of contents. They cannot be linked to directly either.*\n\n## Lists\n\nLists should not go over 4 levels of indentation and should not have an empty line in between each item.\n\nFor nested lists, bullets or numbers must align with the item content of their parent lists.\n\nThe following example was done incorrectly (take note of the spacing before the bullet):\n\n```markdown\n1. Fly a kite\n - Don't fly a kite if it's raining\n```\n\nThe following example was done correctly:\n\n```markdown\n1. Fly a kite\n - Don't fly a kite if it's raining\n```\n\n### Bulleted\n\nBulleted lists must use a hyphen (`-`). These must then be followed by one space. (Example shown below.)\n\n```markdown\n- osu!\n - Hit circle\n - Combo number\n - Approach circle\n - Slider\n - Hit circles\n - Slider body\n - Slider ticks\n - Spinner\n- osu!taiko\n```\n\n### Numbered\n\nThe numbers in a numbered list must be incremented to represent their step.\n\n```markdown\n1. Download the osu! installer.\n2. Run the installer.\n 1. To change the installation location, click the text underneath the progression bar.\n 2. The installer will prompt for a new location, choose the installation folder.\n3. osu! will start up once installation is complete.\n4. Sign in.\n```\n\n### Mixed\n\nCombining both bulleted and numbered lists should be done sparingly.\n\n```markdown\n1. Download a skin from the forums.\n2. Load the skin file into osu!.\n - If the file is a `.zip`, unzip it and move the contents into the `Skins/` folder (found in your osu! installation folder).\n - If the file is a `.osk`, open it on your desktop or drag-and-drop it into the game client.\n3. Open osu!, if it is not opened, and select the skin in the options.\n - This may have been completed if you opened the `.osk` file or drag-and-dropped it into the game client.\n```\n\n## Code\n\nThe markup for code is a grave mark (`` ` ``). To put grave marks in code, use double grave marks instead. If the grave mark is at the start or end, pad it with one space. (Example shown below.)\n\n```markdown\n`` ` ``\n`` `Space` ``\n```\n\n### Keyboard keys\n\n*Notice: When denoting the letter itself, and not the keyboard key, use quotation marks instead.*\n\nWhen representing keyboard keys, use capital letters for single characters and title case for modifiers. Use the plus symbol (`+`) (without code) to represent key combinations. (Example shown below.)\n\n```markdown\npippi is spelt with a lowercase \"p\" like peppy.\n\nPress `Ctrl` + `O` to open the open dialog.\n```\n\nWhen representing a space or the spacebar, use `` `Space` ``.\n\n### Button and menu text\n\nWhen copying the text from a menu or button, the letter casing should be copied as it appears. (Example shown below.)\n\n```markdown\nThe `osu!direct` button is visible in the main menu on the right side, if you have an active osu!supporter tag.\n```\n\n### Folder and directory names\n\nWhen copying the name of a folder or directory, the letter casing should be copied as it appears, but prefer lowercased paths when possible. Directory paths must not be absolute (i.e. do not start the directory name from the drive letter or from the root folder). (Example shown below.)\n\n```markdown\nosu! is installed in the `AppData/Local` folder by default, unless specified otherwise during installation.\n```\n\n### Keywords and commands\n\nWhen copying a keyword or command, the letter casing should be copied as it appears or how someone normally would type it. If applicable, prefer lowercase letters. (Example shown below.)\n\n```markdown\nAs of now, the `Name` and `Author` commands in the skin configuration file (`skin.ini`) do nothing.\n```\n\n### File names\n\nWhen copying the name of a file, the letter casing should be copied as it appears. If applicable, prefer lowercase letters. (Example shown below.)\n\n```markdown\nTo play osu!, double click the `osu!.exe` icon.\n```\n\n### File extensions\n\n*Notice: File formats (not to be confused with file extensions) must be written in capital letters without the prefixed fullstop (`.`).*\n\nFile extensions must be prefixed with a fullstop (`.`) and be followed by the file extension in lowercase letters. (Example shown below.)\n\n```markdown\nThe JPG (or JPEG) file format has the `.jpg` (or `.jpeg`) extension.\n```\n\n### Chat channels\n\nWhen copying the name of a chat channel, start it with a hash (`#`), followed by the channel name in lowercase letters. (Example shown below.)\n\n```markdown\n`#lobby` is where you can advertise your multi room.\n```\n\n## Preformatted text (code blocks)\n\n*Notice: Syntax highlighting for preformatted text is not implemented on the website yet.*\n\nPreformatted text (also known as code blocks) must be fenced using three grave marks. They should set the language identifier for syntax highlighting.\n\n## Links\n\nThere are two types of links: inline and reference. Inline has two styles.\n\nThe following is an example of both inline styles:\n\n```markdown\n[Game Modifiers](/wiki/Game_Modifiers)\n\n\n```\n\nThe following is an example of the reference style:\n\n```markdown\n[Game Modifiers][game mods link]\n\n[game mods link]: /wiki/Game_Modifiers\n```\n\n---\n\nLinks must use the inline style if they are only referenced once. The inline angle brackets style should be avoided. References to reference links must be placed at the bottom of the article.\n\n### Internal links\n\n*Note: Internal links refer to links that stay inside the `https://osu.ppy.sh/` domain.*\n\n#### Wiki links\n\nAll links that point to an wiki article should start with `/wiki/` followed by the path to get to the article you are targeting. Relative links may also be used. Some examples include the following:\n\n```markdown\n[FAQ](/wiki/FAQ)\n[pippi](/wiki/Mascots#-pippi)\n[Beatmaps](../)\n[Pattern](./Pattern)\n```\n\nWiki links must not use redirects and must not have a trailing forward slash (`/`).\n\nBad examples include the following:\n\n```markdown\n[Article styling criteria](/wiki/ASC)\n[Developers](/wiki/Developers/)\n[Developers](/wiki/Developers/#game-client-developers)\n```\n\nGood examples include the following:\n\n```markdown\n[Article styling criteria](/wiki/Article_styling_criteria)\n[Developers](/wiki/Developers)\n[Developers](/wiki/Developers#game-client-developers)\n```\n\n##### Sub-article links\n\nWiki links that point to a sub-article should include the parent article's folder name in its link text. See the following example:\n\n```markdown\n*See also: [Beatmap Editor/Design](/wiki/Beatmap_Editor/Design)*\n```\n\n##### Section links\n\n*Notice: On the website, heading levels 4 and 5 are not given the id attribute. This means that they can not be linked to directly.*\n\nWiki links that point to a section of an article may use the section sign symbol (`§`). See the following example:\n\n```markdown\n*For timing rules, see: [Ranking Criteria § Timing](/wiki/Ranking_Criteria#timing)*\n```\n\n#### Other osu! links\n\nThe URL from the address bar of your web browser should be copied as it is when linking to other osu! web pages. The `https://osu.ppy.sh` part of the URL must be kept.\n\n##### User profiles\n\nAll usernames must be linked on first occurrence. Other occurrences are optional, but must be consistent throughout the entire article for all usernames. If it is difficult to determine the user's id, it may be skipped over.\n\nWhen linking to a user profile, the user's id number must be used. Use the new website (`https://osu.ppy.sh/users/{username})`) to get the user's id.\n\nThe link text of the user link should be the user's current name.\n\n##### Difficulties\n\nWhenever linking to a single difficulty, use this format as the link text:\n\n```\n{artist} - {title} ({creator}) [{difficuty_name}]\n```\n\nThe link must actually link to that difficulty. Beatmap difficulty URLs must be formatted as follows:\n\n```\nhttps://osu.ppy.sh/beatmapsets/{BeatmapSetID}#{mode}/{BeatmapID}\n```\n\nThe difficulty name may be left outside of the link text, but doing so must be consistent throughout the entire article.\n\n##### Beatmaps\n\nWhenever linking to a beatmap, use this format as the link text:\n\n```\n{artist} - {title} ({creator})\n```\n\nAll beatmap URLs must be formatted as follows:\n\n```\nhttps://osu.ppy.sh/beatmapsets/{BeatmapSetID}\n```\n\n### External links\n\n*Notice: External links refers to links that go outside the `https://osu.ppy.sh/` domain.*\n\nThe `https` protocol must be used, unless the site does not support it. External links must be a clean and direct link to a reputable source. The link text should be the title of the page it is linking to. The URL from the address bar of your web browser should be copied as it is when linking to other external pages.\n\nThere are no visual differences between external and osu! web links. Due to this, the website name should be included in the title text. See the following example:\n\n```markdown\n*For more information about music theory, see: [Music theory](https://en.wikipedia.org/wiki/Music_theory \"Wikipedia\")*\n```\n\n## Images\n\nThere are two types of image links: inline and reference. Examples:\n\n**Inline style:**\n\n```markdown\n![](/wiki/shared/flag/AU.gif)\n```\n\n**Reference style:**\n\n```markdown\n![][flag_AU]\n\n[flag_AU]: /wiki/shared/flag/AU.gif\n```\n\nImages should use the inline linking style. References to reference links must be placed at the bottom of the article.\n\nImages must be placed in a folder named `img`, located in the article's folder. Images that are used in multiple articles should be stored in the `/wiki/shared/` folder.\n\n### Image caching\n\nImages on the website are cached for up to 60 days. The cached image is matched with the image link's URL.\n\nWhen updating an image, either change the image's name or append a query string to the URL. In both cases, all translations linking to the updated image should also be updated.\n\n### Formats and quality\n\nImages should use the JPG format at quality 8 (80 or 80%, depending on the program). If the image contains transparency or has text that must be readable, use the PNG format instead. If the image contains an animation, the GIF format can be used; however, this should be used sparingly as these may take longer to load or can be bigger then the [max file size](#file-size).\n\n### File size\n\nImages must be under 1 megabyte, otherwise they will fail to load. Downscaling and using JPG at 80% is almost always under the size limit.\n\nAll images should be optimised as much as possible. Use [jpeg-archive](https://github.com/danielgtaylor/jpeg-archive \"GitHub\") to compress JPEG images. For consistency, use the following command for jpeg-archive:\n\n```sh\njpeg-recompress -am smallfry \n```\n\nWhere `` is the file name to be compressed and `` is the compressed file name.\n\n### File names\n\n*Notice: File extensions must use lowercase letters, otherwise they will fail to load!*\n\nUse hyphens (`-`) when spacing words. When naming an image, the file name should be meaningful or descriptive but short.\n\n### Formatting and positioning\n\n*Note: It is currently not possible to float an image or have text wrap around it.*\n\nImages on the website will be centred when it is on a single line, by themself. Otherwise, they will be positioned inline with the paragraph. The following example will place the image in the center:\n\n```markdown\nInstalling osu! is easy. First, download the installer from the download page.\n\n![](img/download-page.jpg)\n\nThen locate the installer and run it.\n```\n\n### Alt text\n\nImages should have alt text unless it is for decorative purposes.\n\n### Captions\n\nImages are given captions on the website if they fulfill these conditions:\n\n1. The image is by itself.\n2. The image is not inside a heading.\n3. The image has title text.\n\nCaptions are assumed via the title text, which must be in plain text. Images with captions are also centred with the image on the website.\n\n### Max image width\n\nThe website's max image width is the width of the article body. Images should be no wider than 800 pixels.\n\n### Annotating images\n\nWhen annotating images, use *Torus Regular*. For Chinese, Korean, Japanese characters, use *Microsoft YaHei*.\n\nAnnotating images should be avoided, as it is difficult for translators (and other editors) to edit them.\n\n#### Translating annotated images\n\nWhen translating annotated images, the localised image version must be placed in the same directory as the original version (i.e. the English version). The filename of a localised image version must start with the original version's name, followed by a hyphen, followed by the locale name (in capital letters). See the following examples:\n\n- `hardrock-mod-vs-easy-mod.jpg` for English\n- `hardrock-mod-vs-easy-mod-DE.jpg` for German\n- `hardrock-mod-vs-easy-mod-ZH-TW.jpg` for Traditional Chinese\n\n### Screenshots of gameplay\n\nAll screenshots of gameplay must be done in the stable build, unless it is for a specific feature that is unavailable in the stable build. You should use the in-game screenshot feature (`F12`).\n\n#### Game client settings\n\n*Note: If you do not want to change your current settings for the wiki, you can move your `osu!..cfg` out of the osu! folder and move it back later.*\n\nYou must set these settings before taking a screenshot of the game client (settings not stated below are assumed to be at their defaults):\n\n- Select language: `English`\n- Prefer metadata in original language: `Enabled`\n- Resolution: `1280x720`\n- Fullscreen mode: `Disabled`\n- Parallax: `Disabled`\n- Menu tips: `Disabled`\n- Seasonal backgrounds: `Never`\n- Always show key overlay: `Enabled`\n- Current skin: `Default` (first option)\n\n*Notice to translators: If you are translating an article containing screenshots of the game, you may set the game client's language to the language you are translating in.*\n\n### Image links\n\nImages must not be part of a link text.\n\nFlag icons next to user links must be separate from the link text. See the following example:\n\n```markdown\n![][flag_AU] [peppy](https://osu.ppy.sh/users/2)\n```\n\n### Flag icons\n\n*For a list of flag icons, see: [issue \\#328](https://github.com/ppy/osu-wiki/issues/328 \"GitHub\")*\n\nThe flag icons use the two letter code (in all capital letters) and end with `.gif`. When adding a flag inline, use this format:\n\n```markdown\n![](/wiki/shared/flag/xx.gif)\n```\n\nWhere `xx` is the [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 \"Wikipedia\") two-lettered country code for the flag.\n\nThe full country name should be added in the title text. The country code in the alternate text is optional, but must be applied to all flag icons in the article.\n\n## Tables\n\nTables on the website only support headings along the first row.\n\nTables must not be beautified (do not pad cells with extra spaces to make their widths uniform). They must have a vertical bar (`|`) on the left and right sides and the text of each cell must be padded with one space on both sides. Empty cells must use a vertical bar (`|`) followed by two spaces then another vertical bar (`|`).\n\nThe delimiter row (the next line after the table heading) must use only three characters per column (and be padded with a space on both sides), which must look like one of the following:\n\n- `:--` (for left align)\n- `:-:` (for centre align)\n- `--:` (for right align)\n\n---\n\nThe following is an example of what a table should look like:\n\n```markdown\n| Team \"Picturesque\" Red | Score | Team \"Statuesque\" Blue | Average Beatmap Stars |\n| :-- | :-: | --: | :-- |\n| **peppy** | 5 - 2 | pippi | 9.3 stars |\n| Aiko | 1 - 6 | **Alisa** | 4.2 stars |\n| Ryūta | 3 - 4 | **Yuzu** | 5.1 stars |\n| **Taikonator** | 7 - 0 | Tama | 13.37 stars |\n| Maria | No Contest | Mocha | |\n```\n\n## Blockquotes\n\nThe blockquote is limited to quoting text from someone. It must not be used to format text otherwise.\n\n## Thematic breaks\n\nThe thematic break (also known as the horizontal rule or line) should be used sparingly. A few uses of the thematic break may include (but is not limited to):\n\n- separating images from text\n- separating multiple images that follow one another\n- shifting the topic within a section\n\nThese must have an empty line before and after the markup. Thematic breaks must use only three hyphens, as depicted below:\n\n```markdown\n---\n```\n" + "# Formatting\n\n*For the writing standards, see: [Article style criteria/Writing](../Writing)*\\\n*Notice: This article uses [RFC 2119](https://tools.ietf.org/html/rfc2119) to describe requirement levels.*\n\n## Locales\n\nListed below are the properly-supported locales for the wiki:\n\n| File Name | Locale Name | Native Script |\n| :-- | :-- | :-- |\n| `en.md` | English | English |\n| `ar.md` | Arabic | \u0627\u064e\u0644\u0652\u0639\u064e\u0631\u064e\u0628\u0650\u064a\u064e\u0651\u0629\u064f |\n| `be.md` | Belarusian | \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f \u043c\u043e\u0432\u0430 |\n| `bg.md` | Bulgarian | \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438 |\n| `cs.md` | Czech | \u010cesky |\n| `da.md` | Danish | Dansk |\n| `de.md` | German | Deutsch |\n| `el.md` | Greek | \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac |\n| `es.md` | Spanish | Espa\u00f1ol |\n| `fi.md` | Finnish | Suomi |\n| `fr.md` | French | Fran\u00e7ais |\n| `hu.md` | Hungarian | Magyar |\n| `id.md` | Indonesian | Bahasa Indonesia |\n| `it.md` | Italian | Italiano |\n| `ja.md` | Japanese | \u65e5\u672c\u8a9e |\n| `ko.md` | Korean | \ud55c\uad6d\uc5b4 |\n| `nl.md` | Dutch | Nederlands |\n| `no.md` | Norwegian | Norsk |\n| `pl.md` | Polish | Polski |\n| `pt.md` | Portuguese | Portugu\u00eas |\n| `pt-br.md` | Brazilian Portuguese | Portugu\u00eas (Brasil) |\n| `ro.md` | Romanian | Rom\u00e2n\u0103 |\n| `ru.md` | Russian | \u0420\u0443\u0441\u0441\u043a\u0438\u0439 |\n| `sk.md` | Slovak | Sloven\u010dina |\n| `sv.md` | Swedish | Svenska |\n| `th.md` | Thai | \u0e44\u0e17\u0e22 |\n| `tr.md` | Turkish | T\u00fcrk\u00e7e |\n| `uk.md` | Ukrainian | \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430 \u043c\u043e\u0432\u0430 |\n| `vi.md` | Vietnamese | Ti\u1ebfng Vi\u1ec7t |\n| `zh.md` | Chinese (Simplified) | \u7b80\u4f53\u4e2d\u6587 |\n| `zh-tw.md` | Traditional Chinese (Taiwan) | \u7e41\u9ad4\u4e2d\u6587\uff08\u53f0\u7063\uff09 |\n\n*Note: The website will give readers their selected language's version of an article. If it is not available, the English version will be given.*\n\n## Front matter\n\nFront matter must be placed at the very top of the file. It is written in [YAML](https://en.wikipedia.org/wiki/YAML#Example) and describes additional information about the article. This must be surrounded by three hyphens (`---`) on the lines above and below it, and an empty line must follow it before the title heading.\n\n### Articles that need help\n\n*Note: Avoid translating English articles with this tag. In addition to this, this tag should be added when the translation needs its own clean up.*\n\nThe `needs_cleanup` tag may be added to articles that need rewriting or formatting help. It is also acceptable to open an issue on GitHub for this purpose. This tag must be written as shown below:\n\n```yaml\nneeds_cleanup: true\n```\n\nWhen adding this tag to an article, [comments](#comments) should also be added to explain what needs to be done to remove the tag.\n\n### Outdated articles\n\n*Note: Avoid translating English articles with this tag. If the English article has this tag, the translation must also have this tag.*\n\nEnglish articles may become outdated when the content they contain is misleading or no longer relevant. These should receive an `outdated` tag, which must be written as shown below:\n\n```yaml\noutdated: true\n```\n\nWhen adding this tag to an article, [comments](#comments) should also be added to explain what needs to be updated to remove the tag.\n\n### Outdated translations\n\nTranslated articles that are outdated must use the `outdated_translation` tag when the English variant is updated, except for minor wording, grammar changes, and the like, that do not affect the meaning of the article.\n\n```yaml\noutdated_translation: true\n```\n\nWhen outdating translations, they must also receive an `outdated_since` tag that points to the first commit where the English version is updated.\n\n```yaml\noutdated_since: 29eac89cd535f8b071ca000af8fe4f0be22bdc9b\n```\n\n### Tagging articles\n\nTags help the website's search engine query articles better. Tags should be written in the same language as the article and include the original list of tags. Tags should use lowercase letters where applicable.\n\nFor example, an article called \"Beatmap discussion\" may include the following tags:\n\n```yaml\ntags:\n - beatmap discussions\n - modding V2\n - MV2\n```\n\n### Translations without reviews\n\n*Note: Wiki maintainers will determine and apply this mark prior to merging.*\n\nSometimes, translations are added to the wiki without review from other native speakers of the language. In this case, the `no_native_review` mark is added to let future translators know that it may need to be checked again. This tag must be written as shown below:\n\n```yaml\nno_native_review: true\n```\n\n## Article naming\n\n*See also: [Folder names](#folder-names) and [Titles](#titles)*\n\nArticle titles should be singular and use sentence case. See [Wikipedia's naming conventions article](https://en.wikipedia.org/wiki/Wikipedia:Naming_conventions_(plurals)) for more details.\n\nArticle titles should match the folder name it is in (spaces may replace underscores (`_`) where appropriate). If the folder name changes, the article title should be changed to match it and vice versa.\n\n---\n\nContest and tournament articles are an exception. The folder name must use abbreviations, acronyms, or initialisms. The article's title must be the full name of the contest or tournament.\n\n## Folder and file structure\n\n### Folder names\n\n*See also: [Article naming](#article-naming)*\n\nFolder names must be in English and use sentence case.\n\nFolder names must only use these characters:\n\n- uppercase and lowercase letters\n- numbers\n- underscores (`_`)\n- hyphens (`-`)\n- exclamation marks (`!`)\n\n### Article file names\n\nThe file name of an article can be found in the `File Name` column of the [locales section](#locales). The location of a translated article must be placed in the same folder as the English article.\n\n### Index articles\n\nAn index article must be created if the folder is intended to only hold other articles. Index articles must contain a list of articles that are inside its own folder. They may also contain other information, such as a lead paragraph or descriptions of the linked articles.\n\n### Disambiguation articles\n\n[Disambiguation](/wiki/Disambiguation) articles must be placed in the `/wiki/Disambiguation` folder. The main page must be updated to include the disambiguation article. Refer to [Disambiguation/Mod](/wiki/Disambiguation/Mod) as an example.\n\nRedirects must be updated to have the ambiguous keyword(s) redirect to the disambiguation article.\n\nArticles linked from a disambiguation article must have a [For other uses](#for-other-uses) hatnote.\n\n## HTML\n\nHTML must not be used, with exception for [comments](#comments). The structure of the article must be redone if HTML is used.\n\n### Comments\n\nHTML comments should be used for marking to-dos, but may also be used to annotate text. They should be on their own line, but can be placed inline in a paragraph. If placed inline, the start of the comment must not have a space.\n\nBad example:\n\n```markdown\nHTML comments should be used for marking to-dos or annotate text.\n```\n\nGood example:\n\n```markdown\nHTML comments should be used for marking to-dos or annotate text.\n```\n\n## Editing\n\n### End of line sequence\n\n*Caution: Uploading Markdown files using `CRLF` (carriage return and line feed) via GitHub will result in those files using `CRLF`. To prevent this, set the line ending to `LF` (line feed) before uploading.*\n\nMarkdown files must be checked in using the `LF` end of line sequence.\n\n### Escaping\n\nMarkdown syntax should be escaped as needed. However, article titles are parsed as plain text and so must not be escaped.\n\n### Paragraphs\n\nEach paragraph must be followed by one empty line.\n\n### Line breaks\n\nLine breaks must use a backslash (`\\`).\n\nLine breaks must be used sparingly.\n\n## Hatnote\n\n*Not to be confused with [Notice](#notice).*\n\nHatnotes are short notes placed at the top of an article or section to help readers navigate to related articles or inform them about related topics.\n\nHatnotes must be italicised and be placed immediately after the heading. If multiple hatnotes are used, they must be on the same paragraph separated with a line break.\n\n### Main page\n\n*Main page* hatnotes direct the reader to the main article of a topic. When this hatnote is used, it implies that the section it is on is a summary of what the linked page is about. This hatnote should have only one link. These must be formatted as follows:\n\n```markdown\n*Main page: {article}*\n\n*Main pages: {article} and {article}*\n```\n\n### See also\n\n*See also* hatnotes suggest to readers other points of interest from a given article or section. These must be formatted as follows:\n\n```markdown\n*See also: {article}*\n\n*See also: {article} and {article}*\n```\n\n### For see\n\n*For see* hatnotes are similar to *see also* hatnotes, but are generally more descriptive and direct. This hatnote may use more than one link if necessary. These must be formatted as follows:\n\n```markdown\n*For {description}, see: {article}*\n\n*For {description}, see: {article} and {article}*\n```\n\n### Not to be confused with\n\n*Not to be confused with* hatnotes help distinguish ambiguous or misunderstood article titles or sections. This hatnote may use more than one link if necessary. These must be formatted as follows:\n\n```markdown\n*Not to be confused with {article}.*\n\n*Not to be confused with {article} or {article}.*\n```\n\n### For other uses\n\n*For other uses* hatnotes are similar to *not to be confused with* hatnotes, but links directly to the [disambiguation article](#disambiguation-articles). This hatnote must only link to the disambiguation article. These must be formatted as follows:\n\n```markdown\n*For other uses, see {disambiguation article}.*\n```\n\n## Notice\n\n*Not to be confused with [Hatnote](#hatnote).*\n\nA notice should be placed where appropriate in a section, but must start off the paragraph and use italics. Notices may contain bolding where appropriate, but should be kept to a minimum. Notices must be written as complete sentences. Thus, unlike most [hatnotes](#hatnote), notices must use a full stop (`.`) or an exclamation mark (`!`) if appropriate. Anything within the same paragraph of a notice must also be italicised. These must be formatted as follows:\n\n```markdown\n*Note: {note}.*\n\n*Notice: {notice}.*\n\n*Caution: {caution}.*\n\n*Warning: {warning}.*\n```\n\n- `Note` should be used for factual or trivial details.\n- `Notice` should be used for reminders or to draw attention to something that the reader should be made aware of.\n- `Caution` should be used to warn the reader to avoid unintended consequences.\n- `Warning` should be used to warn the reader that action may be taken against them.\n\n## Stacked hatnotes and notices\n\nMultiple hatnotes and notices may be stacked when necessary. When doing this, they must be stacked without blank lines and use trailing backslashes:\n\n```markdown\n*Warning: {warning}.*\\\n*See also: {article}*\n```\n\nIn many cases, it may be more fitting to embed extraneous hatnotes or notices into paragraph text instead of stacking many of them.\n\n## Emphasising\n\n### Bolding\n\nBold text must use double asterisks (`**`).\n\nLead paragraphs may bold the first occurrence of the article's title.\n\n### Italics\n\nItalics must use single asterisks (`*`).\n\nThe first occurrence of an abbreviation, acronym, or initialism may be italicised.\n\nItalics may also be used to provide emphasis or help with readability.\n\nNames of work or video games should be italicised. osu! \u2014 the game \u2014 is exempt from this.\n\nAs an example, when referring to songs in the format of `{artist} - {title}`, the whole part is a reference to the work and should therefore be italicised:\n\n```markdown\n*cYsmix - triangles* is a one of the three intro songs that can be heard when starting the game client.\n```\n\nArtist names are otherwise generally not italicised. This means that in free-form references, only the title should be italicised, because the artist name is then not part of the name of the work:\n\n```markdown\n*Blue Zenith* by xi is an infamous song in the osu! community due to a famous score set by a top player on a certain beatmap.\n```\n\n### Emphasis and links\n\nLinked text appears in a different colour which already provides emphasis, and therefore does not need further emphasis:\n\n```markdown\n[Camellia - OOPARTS](https://cametek.bandcamp.com/track/parts) is an example of a song officially created specifically for osu!, otherwise known as an *osu! original*, since it was specifically commissioned for the osu! World Cup 2020 tiebreaker.\n```\n\nThis however does not apply if the referenced work is not the only part of the link:\n\n```markdown\n[Voltaeyx's beatmap of *TheFatRat - Mayday (feat. Laura Brehm)*](https://osu.ppy.sh/beatmapsets/756794) amassed considerable popularity in 2018 due to its unique overlapping slider patterns.\n```\n\nThe above type of construction should be used sparingly, and must not be used in places with many links, such as tables or lists.\n\n## Headings\n\nAll headings must use sentence case.\n\nHeadings must use the [ATX (hash) style](https://github.github.com/gfm/#atx-headings) and must have an empty line before and after the heading. The title heading is an exception when it is on the first line. If this is the case, there only needs to be an empty line after the title heading.\n\nHeadings must not exceed a heading level of 5 and must not be used to style or format text.\n\n### Titles\n\n*See also: [Article naming](#article-naming)*\\\n*Caution: Titles are parsed as plain text; they must not be escaped.*\n\nThe first heading in all articles must be a level 1 heading, being the article's title. All headings afterwards must be [section headings](#sections). Titles must not contain formatting, links, or images.\n\nThe title heading must be on the first line, unless [front matter](#front-matter) is being used. If that is the case, the title heading must go after it and have an empty line before the title heading.\n\n### Sections\n\nSection headings must use levels 2 to 5. The section heading proceeding the [title heading](#titles) must be a level 2 heading. Unlike titles, section headings may have small image icons.\n\nSection headings must not skip a heading level (i.e. do not go from a level 2 heading to a level 4 heading) and must not contain formatting or links.\n\n*Notice: On the website, heading levels 4 and 5 will not appear in the table of contents.*\n\n### Custom identifiers\n\nIt is possible to redefine a section's identifier, which is used for linking to it directly. Custom identifiers should be used in case the automatically generated ones are too long or contain tricky punctuation marks or images:\n\n\n\n```markdown\n## My cooldown has passed. How do I appeal? {#appeal}\n\n## Common restriction reasons and cooldowns {#common-reasons}\n\n## Ideas for a multiplayer match {id=\u0438\u0434\u0435\u0438-\u0434\u043b\u044f-\u043c\u0443\u043b\u044c\u0442\u0438\u043f\u043b\u0435\u0435\u0440\u0430} \n```\n\nThis feature can also be used for tagging a specific part of the article which doesn't have a heading. Use it sparingly:\n\n```markdown\n> That's it! You're well on your way to becoming an osu! rhythm champion!\n{#tutorial-quote}\n```\n\n## Lists\n\nLists should not go over 4 levels of indentation and should not have an empty line in between each item.\n\nFor nested lists, bullets or numbers must align with the item content of their parent lists.\n\nThe following example was done incorrectly (take note of the spacing before the bullet):\n\n```markdown\n1. Fly a kite\n - Don't fly a kite if it's raining\n```\n\nThe following example was done correctly:\n\n```markdown\n1. Fly a kite\n - Don't fly a kite if it's raining\n```\n\n### Bulleted\n\nBulleted lists must use a hyphen (`-`). These must then be followed by one space. (Example shown below.)\n\n```markdown\n- osu!\n - Hit circle\n - Combo number\n - Approach circle\n - Slider\n - Hit circles\n - Slider body\n - Slider ticks\n - Spinner\n- osu!taiko\n```\n\n### Numbered\n\nThe numbers in a numbered list must be incremented to represent their step.\n\n```markdown\n1. Download the osu! installer.\n2. Run the installer.\n 1. To change the installation location, click the text underneath the progression bar.\n 2. The installer will prompt for a new location, choose the installation folder.\n3. osu! will start up once installation is complete.\n4. Sign in.\n```\n\n### Mixed\n\nCombining both bulleted and numbered lists should be done sparingly.\n\n```markdown\n1. Download a skin from the forums.\n2. Load the skin file into osu!.\n - If the file is a `.zip`, unzip it and move the contents into the `Skins/` folder (found in your osu! installation folder).\n - If the file is a `.osk`, open it on your desktop or drag-and-drop it into the game client.\n3. Open osu!, if it is not opened, and select the skin in the options.\n - This may have been completed if you opened the `.osk` file or drag-and-dropped it into the game client.\n```\n\n## Code\n\nThe markup for code is a grave mark (`` ` ``). To put grave marks in code, use double grave marks instead. If the grave mark is at the start or end, pad it with one space. (Example shown below.)\n\n```markdown\n`` ` ``\n`` `Space` ``\n```\n\n### Keyboard keys\n\n*Notice: When denoting the letter itself, and not the keyboard key, use quotation marks instead.*\n\nWhen representing keyboard keys, use capital letters for single characters and title case for modifiers. Use the plus symbol (`+`) (without code) to represent key combinations. (Example shown below.)\n\n```markdown\npippi is spelt with a lowercase \"p\" like peppy.\n\nPress `Ctrl` + `O` to open the open dialog.\n```\n\nWhen representing a space or the spacebar, use `` `Space` ``.\n\n### Button and menu text\n\nWhen copying the text from a menu or button, the letter casing should be copied as it appears. (Example shown below.)\n\n```markdown\nThe `osu!direct` button is visible in the main menu on the right side, if you have an active osu!supporter tag.\n```\n\n### Folder and directory names\n\nWhen copying the name of a folder or directory, the letter casing should be copied as it appears, but prefer lowercased paths when possible. Directory paths must not be absolute (i.e. do not start the directory name from the drive letter or from the root folder). (Example shown below.)\n\n```markdown\nosu! is installed in the `AppData/Local` folder by default, unless specified otherwise during installation.\n```\n\n### Keywords and commands\n\nWhen copying a keyword or command, the letter casing should be copied as it appears or how someone normally would type it. If applicable, prefer lowercase letters. (Example shown below.)\n\n```markdown\nAs of now, the `Name` and `Author` commands in the skin configuration file (`skin.ini`) do nothing.\n```\n\n### File names\n\nWhen copying the name of a file, the letter casing should be copied as it appears. If applicable, prefer lowercase letters. (Example shown below.)\n\n```markdown\nTo play osu!, double click the `osu!.exe` icon.\n```\n\n### File extensions\n\n*Notice: File formats (not to be confused with file extensions) must be written in capital letters without the prefixed fullstop (`.`).*\n\nFile extensions must be prefixed with a fullstop (`.`) and be followed by the file extension in lowercase letters. (Example shown below.)\n\n```markdown\nThe JPG (or JPEG) file format has the `.jpg` (or `.jpeg`) extension.\n```\n\n### Chat channels\n\nWhen copying the name of a chat channel, start it with a hash (`#`), followed by the channel name in lowercase letters. (Example shown below.)\n\n```markdown\n`#lobby` is where you can advertise your multi room.\n```\n\n## Preformatted text (code blocks)\n\n*Notice: Syntax highlighting for preformatted text is not implemented on the website yet.*\n\nPreformatted text (also known as code blocks) must be fenced using three grave marks. They should set the language identifier for syntax highlighting.\n\n## Links\n\n*See also: [Footnotes](#footnotes)*\n\nThere are two types of links: inline and reference. Inline has two styles.\n\nThe following is an example of both inline styles:\n\n```markdown\n[Game modifier](/wiki/Game_modifier)\n\n\n```\n\nThe following is an example of the reference style:\n\n```markdown\n[Game modifier][game mods link]\n\n[game mods link]: /wiki/Game_modifier\n```\n\n---\n\nLinks must use the inline style if they are only referenced once. The inline angle brackets style should be avoided. References to reference links must be placed at the bottom of the article.\n\n### Internal links\n\n*Note: Internal links refer to links that stay inside the `https://osu.ppy.sh/` domain.*\n\n#### Wiki links\n\nAll links that point to an wiki article should start with `/wiki/` followed by the path to get to the article you are targeting. Relative links may also be used. Some examples include the following:\n\n```markdown\n[FAQ](/wiki/FAQ)\n[pippi](/wiki/Mascots#pippi)\n[Beatmaps](../)\n[Pattern](./Pattern)\n```\n\nWiki links must not use redirects and must not have a trailing forward slash (`/`).\n\nBad examples include the following:\n\n```markdown\n[Article styling criteria](/wiki/ASC)\n[Developers](/wiki/Developers/)\n[Developers](/wiki/Developers/#game-client-developers)\n```\n\nGood examples include the following:\n\n```markdown\n[Article styling criteria](/wiki/Article_styling_criteria)\n[Developers](/wiki/People/The_Team/Developers)\n[Developers](/wiki/People/The_Team/Developers#game-client-developers)\n```\n\n##### Sub-article links\n\nWiki links that point to a sub-article should include the parent article's folder name in its link text. See the following example:\n\n```markdown\n*See also: [Beatmap Editor/Design](/wiki/Client/Beatmap_editor/Design)*\n```\n\n##### Section links\n\n*Notice: On the website, heading levels 4 and 5 are not given the id attribute. This means that they can not be linked to directly.*\n\nWiki links that point to a section of an article may use the section sign symbol (`\u00a7`). See the following example:\n\n```markdown\n*For timing rules, see: [Ranking Criteria \u00a7 Timing](/wiki/Ranking_Criteria#timing)*\n```\n\n#### Other osu! links\n\nThe URL from the address bar of your web browser should be copied as it is when linking to other osu! web pages. The `https://osu.ppy.sh` part of the URL must be kept.\n\n##### User profiles\n\nAll usernames must be linked on first occurrence. Other occurrences are optional, but must be consistent throughout the entire article for all usernames. If it is difficult to determine the user's id, it may be skipped over.\n\nWhen linking to a user profile, the user's id number must be used. Use the new website (`https://osu.ppy.sh/users/{username})`) to get the user's id.\n\nThe link text of the user link should be the user's current name.\n\n##### Difficulties\n\nWhenever linking to a single difficulty, use this format as the link text:\n\n```\n{artist} - {title} ({creator}) [{difficulty_name}]\n```\n\nThe link must actually link to that difficulty. Beatmap difficulty URLs must be formatted as follows:\n\n```\nhttps://osu.ppy.sh/beatmapsets/{BeatmapSetID}#{mode}/{BeatmapID}\n```\n\nThe difficulty name may be left outside of the link text, but doing so must be consistent throughout the entire article.\n\n##### Beatmaps\n\nWhenever linking to a beatmap, use this format as the link text:\n\n```\n{artist} - {title} ({creator})\n```\n\nAll beatmap URLs must be formatted as follows:\n\n```\nhttps://osu.ppy.sh/beatmapsets/{BeatmapSetID}\n```\n\n### External links\n\n*Notice: External links refers to links that go outside the `https://osu.ppy.sh/` domain.*\n\nThe `https` protocol must be used, unless the site does not support it. External links must be a clean and direct link to a reputable source. The URL from the address bar of your web browser should be copied as it is when linking to other external pages.\n\nThere are no visual differences between external and osu! web links. Due to this, the website name should be included in the title text. See the following example:\n\n```markdown\n*For more information about music theory, see: [Music theory](https://en.wikipedia.org/wiki/Music_theory)*\n```\n\n## Images\n\nThere are two types of image links: inline and reference. Examples:\n\n**Inline style:**\n\n```markdown\n![Gold crown](/wiki/shared/crown-gold.png \"1st place\")\n```\n\n**Reference style:**\n\n```markdown\n![Gold crown][GCrown]\n\n[GCrown]: /wiki/shared/crown-gold.png \"1st place\"\n```\n\nImages should use the inline linking style. Reference link definitions must be placed at the bottom of the article.\n\nAll block images on the page (that have nothing else on the same line) are combined into a single gallery, which can be navigated using arrow icons on both sides of the screen, keyboard shortcuts, or screen swipes on mobile devices.\n\n### Alternative and title text\n\nThe text in the first pair of square brackets (*alternative text*) should describe the image literally. It is used by screen readers or when the image fails to load. It can be omitted if it is identical to the title text or if the image is included only for decorative purposes.\n\nThe text in the quotation marks (*title text*) should give additional context to the image or indicate its meaning. It is displayed as a tooltip when hovering over the image and used as a caption if applicable. It does not support any markdown formatting.\n\n### Display\n\nIf an image is the sole content of a paragraph, it displays as a centred block. Otherwise, it flows with the surrounding inline text.\n\nBlock images with title text display the title text as a caption below the image. Avoid adding [HTML comment](#comments) or any other text on the same line as the image, as this will cause the caption not to be rendered.\n\nBlock images are commonly paired with [infobox](#infoboxes) formatting to reduce their initial size and float them to the side of other content:\n\n```markdown\n::: Infobox\n![](img/mod-response.png \"An example of a response to a mod\")\n:::\n```\n\n### Image caching\n\nImages on the website are cached for up to 60 days. The cached image is matched with the image link's URL.\n\nWhen updating an image, either change the image's name or append a query string to the URL. In both cases, all translations linking to the updated image should also be updated.\n\n### Formats and quality\n\nImages should use the JPG format at quality 8 (80 or 80%, depending on the program). If the image contains transparency or has text that must be readable, use the PNG format instead. If the image contains an animation, the GIF format can be used; however, this should be used sparingly as these may take longer to load or can be bigger then the [max file size](#file-size).\n\n### File size\n\nImages must be under 1 megabyte, otherwise they will fail to load. Downscaling and using JPG at 80% is almost always under the size limit.\n\nAll images should be optimised as much as possible. Use [jpeg-archive](https://github.com/danielgtaylor/jpeg-archive) to compress JPEG images. For consistency, use the following command for jpeg-archive:\n\n```sh\njpeg-recompress -am smallfry \n```\n\nWhere `` is the file name to be compressed and `` is the compressed file name.\n\n### File names\n\n*Notice: File extensions must use lowercase letters, otherwise they will fail to load!*\n\nUse hyphens (`-`) when spacing words. When naming an image, the file name should be meaningful or descriptive but short.\n\nImages must be placed in a folder named `img` under the article's folder. Images that are used in multiple articles should be stored in the `/wiki/shared` folder.\n\n### Max image width\n\nThe website's max image width is the width of the article body. Images should be no wider than 800 pixels.\n\n### Annotating images\n\nWhen annotating images, use *Torus Regular*. For Chinese, Korean, Japanese characters, use *Microsoft YaHei*.\n\nAnnotating images should be avoided, as it is difficult for translators (and other editors) to edit them.\n\n#### Translating annotated images\n\nWhen translating annotated images, the localised image version must be placed in the same directory as the original version (i.e. the English version). The filename of a localised image version must start with the original version's name, followed by a hyphen, followed by the locale name (in capital letters). See the following examples:\n\n- `hardrock-mod-vs-easy-mod.jpg` for English\n- `hardrock-mod-vs-easy-mod-DE.jpg` for German\n- `hardrock-mod-vs-easy-mod-ZH-TW.jpg` for Traditional Chinese\n\n### Screenshots of gameplay\n\nAll screenshots of gameplay must be done in the stable build, unless it is for a specific feature that is unavailable in the stable build. You should use the in-game screenshot feature (`F12`).\n\n#### Game client settings\n\n*Note: If you do not want to change your current settings for the wiki, you can move your `osu!..cfg` out of the osu! folder and move it back later.*\n\nYou must set these settings before taking a screenshot of the game client (settings not stated below are assumed to be at their defaults):\n\n- Select language: `English`\n- Prefer metadata in original language: `Enabled`\n- Resolution: `1280x720`\n- Fullscreen mode: `Disabled`\n- Parallax: `Disabled`\n- Menu tips: `Disabled`\n- Seasonal backgrounds: `Never`\n- Always show key overlay: `Enabled`\n- Current skin: `Default` (first option)\n\n*Notice to translators: If you are translating an article containing screenshots of the game, you may set the game client's language to the language you are translating in.*\n\n### Image links\n\nImages must not be part of a link text.\n\n## Flag icons\n\nThe flag icons use the two letter code (in all capital letters) to match a certain territory. When adding a flag inline, use this format:\n\n```markdown\n::{ flag=XX }::\n```\n\nWhere `XX` is the [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) two-lettered country code for the flag.\n\n## Tables\n\nTables on the website only support headings along the first row.\n\nTables must not be beautified (do not pad cells with extra spaces to make their widths uniform). They must have a vertical bar (`|`) on the left and right sides and the text of each cell must be padded with one space on both sides. Empty cells must use a vertical bar (`|`) followed by two spaces then another vertical bar (`|`).\n\nThe delimiter row (the next line after the table heading) must use only three characters per column (and be padded with a space on both sides), which must look like one of the following:\n\n- `:--` (for left align)\n- `:-:` (for centre align)\n- `--:` (for right align)\n\n---\n\nThe following is an example of what a table should look like:\n\n```markdown\n| Team \"Picturesque\" Red | Score | Team \"Statuesque\" Blue | Average Beatmap Stars |\n| :-- | :-: | --: | :-- |\n| **peppy** | 5 - 2 | pippi | 9.3 stars |\n| Aiko | 1 - 6 | **Alisa** | 4.2 stars |\n| Ry\u016bta | 3 - 4 | **Yuzu** | 5.1 stars |\n| **Taikonator** | 7 - 0 | Tama | 13.37 stars |\n| Maria | No Contest | Mocha | |\n```\n\n## Infoboxes\n\nAn infobox is a fixed-width block which is aligned to the right side of the article. It may contain a relevant image, which explains the surrounding text, or a block of navigation that links to other articles from the same category.\n\nExample use, rendered on the right:\n\n\n\n::: Infobox\n![](/wiki/shared/mods/SD.png \"Sudden Death mod icon\")\n:::\n\n```markdown\n::: Infobox\n![](/wiki/shared/mods/SD.png \"Sudden Death mod icon\")\n:::\n```\n\nInfoboxes should be used with caution in the following cases:\n\n- Short sections: the next section's heading appears below any infoboxes, leaving a large gap after the text.\n- Several images at once: instead, use individual infoboxes for better design.\n\n## Footnotes\n\nFootnotes are short notes located at the end of the page. They are used for citing sources, or providing background information that would otherwise disrupt the flow of the article. Footnotes may contain text formatting and links.\n\nIn the osu! wiki, footnotes are implemented using special syntax (`[^identifier]`). Footnotes can use any identifier, but they will automatically be rendered as superscripts with increasing numbers in order of their first appearance. Translations must not modify identifiers of footnotes.\n\nFootnote references are placed directly after the words, phrases, or sentences they explain, with no space in between. These references must be placed after punctuation, except for parentheses, when they pertain to the contents inside, and dashes.\n\nThe footnotes themselves must be placed in a separate second-level heading at the end of the article. Depending on the content, the heading used may be `References`, `Notes`, or `Notes and references`.\n\nCorrect usage examples:\n\n```markdown\nThe osu! wiki is a project that was meant to replace the old FAQ system.[^wiki-faq] It was named after the rhythm game osu![^osu] and the largest open online encyclopedia, Wikipedia. From the very start, it had attracted skillful translators[^wiki-tl] and editors.\n\n## References\n\n[^wiki-faq]: https://osu.ppy.sh/community/forums/topics/68525\n[^wiki-tl]: https://osu.ppy.sh/community/forums/posts/1177500\n[^osu]: https://osu.ppy.sh/community/forums/posts/1178153\n```\n\n### Citations\n\nCitations, or references, are used to identify a source of information. Citations via footnotes should be preferred over inline links.\n\nReferences should whenever applicable specify author, date, service/platform, and title. The exact format may vary depending on the referenced material with a preference for brevity.\n\nExamples:\n\n```markdown\nThe first version of the osu!api was made available on July 2, 2013.[^api-first-usage] It had received critical acclaim from users.[^api-praise] A new version of API, released several years later, contains many more capabilities.[^api-v2-2020] Endpoint versioning is common among web APIs.[^web-api]\n\n## References\n\n[^api-first-usage]: [Forum thread by peppy (2013-07-02) \"osu!api open beta\"](https://osu.ppy.sh/community/forums/posts/2403913)\n[^api-praise]: [Forum post by Menchi (2013-11-02) in \"osu!api open beta\"](https://osu.ppy.sh/community/forums/posts/2662247)\n[^api-v2-2020]: [Tweet by @ppy (2020-03-20)](https://twitter.com/ppy/status/1263083636363948032)\n[^web-api]: [\"Web API\" on Wikipedia](https://en.wikipedia.org/wiki/Web_API)\n```\n\n### Notes\n\nFootnotes may be used for storing explanations or tangential remarks which cannot be inlined without worsening the article's readability, or are less significant than the article itself. Such footnotes may use free-form text.\n\nExample:\n\n```markdown\nA tournament must not be organised and run by an inexperienced team of unaccomplished and irreputable staff.[^staff]\n\n## Notes\n\n[^staff]: An *inexperienced* staff member is loosely defined as someone who has been playing osu! for less than an hour in total.\n```\n\n## Blockquotes\n\nThe blockquote is limited to [quoting someone or something](/wiki/Article_styling_criteria/Writing#block-quotation). It must not be used to format text otherwise.\n\n```markdown\n> plz enjoy game\n\n\u2014rrtyui\n```\n\n## Thematic breaks\n\nThe thematic break (also known as the horizontal rule or line) should be used sparingly. A few uses of the thematic break may include (but is not limited to):\n\n- separating images from text\n- separating multiple images that follow one another\n- shifting the topic within a section\n\nThese must have an empty line before and after the markup. Thematic breaks must use only three hyphens, as depicted below:\n\n```markdown\n---\n```\n" }; // From https://osu.ppy.sh/api/v2/wiki/en/Article_styling_criteria From 0309751d551afdec7467b6811fff8f330a5619fe Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 Oct 2022 21:50:31 +0300 Subject: [PATCH 241/295] Add test coverage --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 39432ee059..31a866b8f5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -189,6 +189,16 @@ Line after image"; }); } + [Test] + public void TestFlag() + { + AddStep("Add flag", () => + { + markdownContainer.CurrentPath = @"https://dev.ppy.sh"; + markdownContainer.Text = "::{flag=\"AU\"}::"; + }); + } + private class TestMarkdownContainer : WikiMarkdownContainer { public LinkInline Link; From b22e201289ec3e82e0742c8837ca9528060ce92f Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 8 Oct 2022 23:43:23 +0200 Subject: [PATCH 242/295] Fixed stream convert float precision edge case --- .../Editor/TestSceneSliderStreamConversion.cs | 31 +++++++++++++++++++ .../Sliders/SliderSelectionBlueprint.cs | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs index 51871dd9e5..0601dc6068 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs @@ -148,6 +148,37 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }); } + [Test] + public void TestFloatEdgeCaseConversion() + { + Slider slider = null; + + AddStep("select first slider", () => + { + slider = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider); + EditorClock.Seek(slider.StartTime); + EditorBeatmap.SelectedHitObjects.Add(slider); + }); + + AddStep("change to these specific circumstances", () => + { + EditorBeatmap.Difficulty.SliderMultiplier = 1; + var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(slider.StartTime); + timingPoint.BeatLength = 352.941176470588; + slider.Path.ControlPoints[^1].Position = new Vector2(-110, 16); + slider.Path.ExpectedDistance.Value = 100; + }); + + convertToStream(); + + AddAssert("stream created", () => streamCreatedFor(slider, + (time: 0, pathPosition: 0), + (time: 0.25, pathPosition: 0.25), + (time: 0.5, pathPosition: 0.5), + (time: 0.75, pathPosition: 0.75), + (time: 1, pathPosition: 1))); + } + private bool streamCreatedFor(Slider slider, params (double time, double pathPosition)[] expectedCircles) { if (EditorBeatmap.HitObjects.Contains(slider)) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 7c289b5b05..265a1d21b1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -342,7 +342,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders double positionWithRepeats = (time - HitObject.StartTime) / HitObject.Duration * HitObject.SpanCount(); double pathPosition = positionWithRepeats - (int)positionWithRepeats; // every second span is in the reverse direction - need to reverse the path position. - if (Precision.AlmostBigger(positionWithRepeats % 2, 1)) + if (positionWithRepeats % 2 >= 1) pathPosition = 1 - pathPosition; Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition); From c89a55043e68e7af3b8a656d803e5e54568d4b60 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 9 Oct 2022 15:28:12 +0300 Subject: [PATCH 243/295] Fix smoke being blocked with "Relax" mod enabled --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index dec965e567..c86609abd7 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -3,8 +3,10 @@ #nullable disable +using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -60,17 +62,14 @@ namespace osu.Game.Rulesets.Osu { public bool AllowUserPresses = true; + private static readonly OsuAction[] all_actions = (OsuAction[])Enum.GetValues(typeof(OsuAction)); + + protected override IEnumerable BlockedActions => !AllowUserPresses ? all_actions.Where(a => a != OsuAction.Smoke) : Enumerable.Empty(); + public OsuKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) { } - - protected override bool Handle(UIEvent e) - { - if (!AllowUserPresses) return false; - - return base.Handle(e); - } } } From 2d4f390372b9624fdd35dc148b599c25cef5f65d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 9 Oct 2022 17:14:16 +0300 Subject: [PATCH 244/295] Remove non-smoke key bindings on "Relax" mod instead --- .../ManiaInputTestScene.cs | 4 +++- osu.Game.Rulesets.Osu/OsuInputManager.cs | 24 +++++++++++++++---- .../Bindings/DatabasedKeyBindingContainer.cs | 6 ++--- osu.Game/Rulesets/UI/RulesetInputManager.cs | 4 ++-- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs index eb380c07a6..e456659ac4 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs @@ -3,9 +3,11 @@ #nullable disable +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests @@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.Tests { } - protected override void ReloadMappings() + protected override void ReloadMappings(IQueryable realmKeyBindings) { KeyBindings = DefaultKeyBindings; } diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index c86609abd7..14b1c0f1a5 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -11,6 +10,7 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Input.StateChanges.Events; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu @@ -60,16 +60,30 @@ namespace osu.Game.Rulesets.Osu private class OsuKeyBindingContainer : RulesetKeyBindingContainer { - public bool AllowUserPresses = true; + private bool allowUserPresses = true; - private static readonly OsuAction[] all_actions = (OsuAction[])Enum.GetValues(typeof(OsuAction)); - - protected override IEnumerable BlockedActions => !AllowUserPresses ? all_actions.Where(a => a != OsuAction.Smoke) : Enumerable.Empty(); + public bool AllowUserPresses + { + get => allowUserPresses; + set + { + allowUserPresses = value; + ReloadMappings(); + } + } public OsuKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) { } + + protected override void ReloadMappings(IQueryable realmKeyBindings) + { + base.ReloadMappings(realmKeyBindings); + + if (!AllowUserPresses) + KeyBindings = KeyBindings.Where(b => b.GetAction() == OsuAction.Smoke).ToList(); + } } } diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index 14a041b459..4f079ab435 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -55,13 +55,13 @@ namespace osu.Game.Input.Bindings { // The first fire of this is a bit redundant as this is being called in base.LoadComplete, // but this is safest in case the subscription is restored after a context recycle. - reloadMappings(sender.AsQueryable()); + ReloadMappings(sender.AsQueryable()); }); base.LoadComplete(); } - protected override void ReloadMappings() => reloadMappings(queryRealmKeyBindings(realm.Realm)); + protected sealed override void ReloadMappings() => ReloadMappings(queryRealmKeyBindings(realm.Realm)); private IQueryable queryRealmKeyBindings(Realm realm) { @@ -70,7 +70,7 @@ namespace osu.Game.Input.Bindings .Where(b => b.RulesetName == rulesetName && b.Variant == variant); } - private void reloadMappings(IQueryable realmKeyBindings) + protected virtual void ReloadMappings(IQueryable realmKeyBindings) { var defaults = DefaultKeyBindings.ToList(); diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 1a97153f2f..64ac021204 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -230,9 +230,9 @@ namespace osu.Game.Rulesets.UI { } - protected override void ReloadMappings() + protected override void ReloadMappings(IQueryable realmKeyBindings) { - base.ReloadMappings(); + base.ReloadMappings(realmKeyBindings); KeyBindings = KeyBindings.Where(b => RealmKeyBindingStore.CheckValidForGameplay(b.KeyCombination)).ToList(); } From f219c0886d5875ba1e4632704f8f85741222b079 Mon Sep 17 00:00:00 2001 From: pfg Date: Sun, 9 Oct 2022 10:19:21 -0400 Subject: [PATCH 245/295] Add a test for realm preservation --- .../Beatmaps/WorkingBeatmapManagerTest.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs index f14288e7ba..8e8d45937a 100644 --- a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs +++ b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs @@ -9,8 +9,10 @@ using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Collections; using osu.Game.Database; using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Tests.Resources; using osu.Game.Tests.Visual; @@ -96,5 +98,39 @@ namespace osu.Game.Tests.Beatmaps var second = beatmaps.GetWorkingBeatmap(beatmap, true); Assert.That(first, Is.Not.SameAs(second)); }); + + [Test] + public void TestSavePreservesCollections() => AddStep("run test", () => + { + var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID).Detach()); + + var map = beatmaps.GetWorkingBeatmap(beatmap); + + Assert.That(map.BeatmapInfo.BeatmapSet?.Files, Has.Count.GreaterThan(0)); + + string initialHash = map.BeatmapInfo.MD5Hash; + + var preserveCollection = new BeatmapCollection("test save preserves collection"); + preserveCollection.BeatmapMD5Hashes.Add(initialHash); + var noNewCollection = new BeatmapCollection("test save does not introduce new collection"); + Realm.Write(r => + { + r.Add(preserveCollection); + r.Add(noNewCollection); + }); + + Assert.That(preserveCollection.BeatmapMD5Hashes, Does.Contain(initialHash)); + Assert.That(noNewCollection.BeatmapMD5Hashes, Does.Not.Contain(initialHash)); + + beatmaps.Save(map.BeatmapInfo, map.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + + string finalHash = map.BeatmapInfo.MD5Hash; + + Assert.That(finalHash, Is.Not.SameAs(initialHash)); + + Assert.That(preserveCollection.BeatmapMD5Hashes, Does.Not.Contain(initialHash)); + Assert.That(preserveCollection.BeatmapMD5Hashes, Does.Contain(finalHash)); + Assert.That(noNewCollection.BeatmapMD5Hashes, Does.Not.Contain(finalHash)); + }); } } From 5d2e3dcf4af4adac45faa2265bce4e25e5701037 Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Sun, 9 Oct 2022 22:52:54 +0200 Subject: [PATCH 246/295] Remove leftover Enum --- osu.Game/Skinning/HUDSkinComponents.cs | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 osu.Game/Skinning/HUDSkinComponents.cs diff --git a/osu.Game/Skinning/HUDSkinComponents.cs b/osu.Game/Skinning/HUDSkinComponents.cs deleted file mode 100644 index 586882d790..0000000000 --- a/osu.Game/Skinning/HUDSkinComponents.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. - -#nullable disable - -namespace osu.Game.Skinning -{ - public enum HUDSkinComponents - { - ComboCounter, - ScoreCounter, - AccuracyCounter, - HealthDisplay, - SongProgress, - BarHitErrorMeter, - ColourHitErrorMeter, - } -} From 0b5f8e1c93e47176ad132ea79a62da4db4c419fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 13:12:12 +0900 Subject: [PATCH 247/295] Remove duplicated colour in osu!mania argon skin column colouring There will probably be more change to colours, but let's just fix this for now. --- .../Argon/ManiaArgonSkinTransformer.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 27926c11eb..ae313e0b91 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.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.Graphics; using osu.Game.Beatmaps; @@ -89,13 +90,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon Color4 colour; + const int total_colours = 7; + if (stage.IsSpecialColumn(column)) colour = new Color4(159, 101, 255, 255); else { - switch (column % 8) + switch (column % total_colours) { - default: + case 0: colour = new Color4(240, 216, 0, 255); break; @@ -112,20 +115,19 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon break; case 4: - colour = new Color4(178, 0, 240, 255); - break; - - case 5: colour = new Color4(0, 96, 240, 255); break; - case 6: + case 5: colour = new Color4(0, 226, 240, 255); break; - case 7: + case 6: colour = new Color4(0, 240, 96, 255); break; + + default: + throw new ArgumentOutOfRangeException(); } } From 26a473e85e7958ba9fd93d85ec5700d000e57e10 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 14:24:32 +0900 Subject: [PATCH 248/295] Update resources (fixes osu!mania flashlight crash) --- 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 2a678f1c61..692e817f8f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index efdb6c6995..35fdb82230 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 47872e4ff7..105a20c9a4 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + From 33620b7bd62a90249dc2c81cbc0763bfdc0015cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 15:24:17 +0900 Subject: [PATCH 249/295] Fix slider ball facing incorrect direction during rewinding in editor Closes https://github.com/ppy/osu/issues/20648. --- .../Objects/Drawables/DrawableSliderBall.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index 6bfb4e8aae..a2fe623897 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -186,17 +186,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Vector2? lastPosition; + private bool rewinding; + public void UpdateProgress(double completionProgress) { Position = drawableSlider.HitObject.CurvePositionAt(completionProgress); var diff = lastPosition.HasValue ? lastPosition.Value - Position : Position - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f); + if (Clock.ElapsedFrameTime != 0) + rewinding = Clock.ElapsedFrameTime < 0; + // Ensure the value is substantially high enough to allow for Atan2 to get a valid angle. if (diff.LengthFast < 0.01f) return; - ball.Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI); + ball.Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI) + (rewinding ? 180 : 0); lastPosition = Position; } } From 7726dda97568e71e13cf3c9068e3e50c2bbe3696 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 15:31:45 +0900 Subject: [PATCH 250/295] Remove whitespace --- osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs index 8e8d45937a..006326fd3c 100644 --- a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs +++ b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Beatmaps Assert.That(preserveCollection.BeatmapMD5Hashes, Does.Contain(initialHash)); Assert.That(noNewCollection.BeatmapMD5Hashes, Does.Not.Contain(initialHash)); - + beatmaps.Save(map.BeatmapInfo, map.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); string finalHash = map.BeatmapInfo.MD5Hash; From 5a5f3af27d3aa3392f1a45a5388c575c3494c389 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 15:42:08 +0900 Subject: [PATCH 251/295] Rename property and add xmldoc --- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 6 +++-- osu.Game.Rulesets.Osu/OsuInputManager.cs | 28 +++++++++++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index fac1cbfd47..753de6231a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer { public override LocalisableString Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray(); + + public override Type[] IncompatibleMods => + base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray(); /// /// How early before a hitobject's start time to trigger a hit. @@ -51,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Mods return; } - osuInputManager.AllowUserPresses = false; + osuInputManager.AllowGameplayInputs = false; } public void Update(Playfield playfield) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 14b1c0f1a5..1e59e19246 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -19,9 +19,16 @@ namespace osu.Game.Rulesets.Osu { public IEnumerable PressedActions => KeyBindingContainer.PressedActions; - public bool AllowUserPresses + /// + /// Whether gameplay input buttons should be allowed. + /// Defaults to true, generally used for mods like Relax which turn off main inputs. + /// + /// + /// Of note, auxiliary inputs like the "smoke" key are left usable. + /// + public bool AllowGameplayInputs { - set => ((OsuKeyBindingContainer)KeyBindingContainer).AllowUserPresses = value; + set => ((OsuKeyBindingContainer)KeyBindingContainer).AllowGameplayInputs = value; } /// @@ -60,14 +67,21 @@ namespace osu.Game.Rulesets.Osu private class OsuKeyBindingContainer : RulesetKeyBindingContainer { - private bool allowUserPresses = true; + private bool allowGameplayInputs = true; - public bool AllowUserPresses + /// + /// Whether gameplay input buttons should be allowed. + /// Defaults to true, generally used for mods like Relax which turn off main inputs. + /// + /// + /// Of note, auxiliary inputs like the "smoke" key are left usable. + /// + public bool AllowGameplayInputs { - get => allowUserPresses; + get => allowGameplayInputs; set { - allowUserPresses = value; + allowGameplayInputs = value; ReloadMappings(); } } @@ -81,7 +95,7 @@ namespace osu.Game.Rulesets.Osu { base.ReloadMappings(realmKeyBindings); - if (!AllowUserPresses) + if (!AllowGameplayInputs) KeyBindings = KeyBindings.Where(b => b.GetAction() == OsuAction.Smoke).ToList(); } } From f6a8cc3f32d4acfdb2253f593679ab83583d8c6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 16:05:38 +0900 Subject: [PATCH 252/295] Add test showing broken input in HUD overlay when hidden --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index da6604a653..a984f508ea 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Skinning; using osu.Game.Tests.Gameplay; @@ -148,6 +149,42 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent); } + [Test] + public void TestInputDoesntWorkWhenHUDHidden() + { + SongProgressBar getSongProgress() => hudOverlay.ChildrenOfType().Single(); + + bool seeked = false; + + createNew(); + + AddStep("bind seek", () => + { + seeked = false; + + var progress = getSongProgress(); + + progress.ShowHandle = true; + progress.OnSeek += _ => seeked = true; + }); + + AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false); + AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent); + + AddStep("attempt seek", () => + { + InputManager.MoveMouseTo(getSongProgress()); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("seek not performed", () => !seeked); + + AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true); + + AddStep("attempt seek", () => InputManager.Click(MouseButton.Left)); + AddAssert("seek performed", () => seeked); + } + [Test] public void TestHiddenHUDDoesntBlockComponentUpdates() { From 20adc522b9e7e054483c6387ff9f21d64f4367f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 15:57:41 +0900 Subject: [PATCH 253/295] Fix HUD components being interactive even when the HUD is visually hidden --- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 3fbb051c3b..7833c2d7fa 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -39,6 +39,10 @@ namespace osu.Game.Screens.Play /// public float BottomScoringElementsHeight { get; private set; } + // HUD uses AlwaysVisible on child components so they can be in an updated state for next display. + // Without blocking input, this would also allow them to be interacted with in such a state. + public override bool PropagatePositionalInputSubTree => ShowHud.Value; + public readonly KeyCounterDisplay KeyCounter; public readonly ModDisplay ModDisplay; public readonly HoldForMenuButton HoldToQuit; From b783fdf667d03aa6cddc3ccf1fffa704e7552c3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 16:20:17 +0900 Subject: [PATCH 254/295] Fix clicking a leaderboard score at song select transtiioning briefly to main menu - Regressed in #20567. - Closes #20651. --- osu.Game/OsuGame.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 939d3a63ed..1716e48395 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -563,6 +563,15 @@ namespace osu.Game // 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). + // + // As a special case, if the beatmap and ruleset already match, allow immediately displaying the score from song select. + // This is guaranteed to not crash, and feels better from a user's perspective (ie. if they are clicking a score in the + // song select leaderboard). + IEnumerable validScreens = + Beatmap.Value.BeatmapInfo.Equals(databasedBeatmap) && Ruleset.Value.Equals(databasedScore.ScoreInfo.Ruleset) + ? new[] { typeof(SongSelect) } + : Array.Empty(); + PerformFromScreen(screen => { Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset}) to match score"); @@ -580,7 +589,7 @@ namespace osu.Game screen.Push(new SoloResultsScreen(databasedScore.ScoreInfo, false)); break; } - }); + }, validScreens: validScreens); } public override Task Import(params ImportTask[] imports) From eae32ca4839c3779176a69a0099a257e8f789192 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Mon, 10 Oct 2022 09:39:40 +0200 Subject: [PATCH 255/295] Switch `DefinitelyBigger` to `AlmostBigger` to account for fp errors --- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index 85bff630e3..58d00801a5 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Objects startTime += barLength; } - for (double t = startTime; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) + for (double t = startTime; Precision.AlmostBigger(endTime, t); t += barLength, currentBeat++) { double roundedTime = Math.Round(t, MidpointRounding.AwayFromZero); From 28185178d60ab2ff188fc15d7ebd417ff9aacdfa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Oct 2022 16:51:50 +0900 Subject: [PATCH 256/295] Add support for weird storyboards which have backwards events Closes https://github.com/ppy/osu/issues/20632. Not adding test coverage because this is just weird. --- osu.Game/Storyboards/CommandTimeline.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index 4d0da9597b..d1a1edcd03 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -27,7 +27,10 @@ namespace osu.Game.Storyboards public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue) { if (endTime < startTime) - return; + { + (startTime, endTime) = (endTime, startTime); + (startValue, endValue) = (endValue, startValue); + } commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue }); From d389808427e6d693240df30a8412f432e0b08a1f Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Mon, 10 Oct 2022 14:00:26 +0200 Subject: [PATCH 257/295] Make effect point search more efficient --- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index 58d00801a5..9105a78741 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Objects double lastHitTime = 1 + lastObject.GetEndTime(); var timingPoints = beatmap.ControlPointInfo.TimingPoints; - var effectPoints = beatmap.ControlPointInfo.EffectPoints; if (timingPoints.Count == 0) return; @@ -39,14 +38,13 @@ namespace osu.Game.Rulesets.Objects for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint currentTimingPoint = timingPoints[i]; - EffectControlPoint? currentEffectPoint = effectPoints.FirstOrDefault(p => p.Time == currentTimingPoint.Time); + EffectControlPoint? currentEffectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTimingPoint.Time); int currentBeat = 0; - // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object + // Stop on the next timing point, or if there is no next timing point stop slightly past the last object double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastHitTime + currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; double startTime = currentTimingPoint.Time; - double barLength = currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; if (currentEffectPoint != null && currentEffectPoint.OmitFirstBarLine) From e3b405cc23e51de735d49110358f3e739c677321 Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:02:23 +0800 Subject: [PATCH 258/295] Update IWorkingBeatmap.cs --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index a39766abe1..cf72a611e5 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -134,6 +134,6 @@ namespace osu.Game.Beatmaps /// /// Reads the correct track restart point from beatmap metadata and sets looping to enabled. /// - void PrepareTrackForPreview(bool looping); + void PrepareTrackForPreview(bool looping, int priorToPreview = 0); } } From 749dc400627d45d8c6a8f101f8c573e6e961b0fd Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:03:11 +0800 Subject: [PATCH 259/295] Update WorkingBeatmap.cs --- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 0a51c843cd..8bd0f4b14b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -110,10 +110,10 @@ namespace osu.Game.Beatmaps public Track LoadTrack() => track = GetBeatmapTrack() ?? GetVirtualTrack(1000); - public void PrepareTrackForPreview(bool looping) + public void PrepareTrackForPreview(bool looping, int priorToPreview = 2000) { Track.Looping = looping; - Track.RestartPoint = Metadata.PreviewTime; + Track.RestartPoint = Metadata.PreviewTime - priorToPreview; if (Track.RestartPoint == -1) { From 9a3689b6e96acdef198a57c981b12b1267039934 Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:04:14 +0800 Subject: [PATCH 260/295] Update IntroCircles.cs --- osu.Game/Screens/Menu/IntroCircles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroCircles.cs b/osu.Game/Screens/Menu/IntroCircles.cs index 7a4bdb231f..a69f2c8a65 100644 --- a/osu.Game/Screens/Menu/IntroCircles.cs +++ b/osu.Game/Screens/Menu/IntroCircles.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Menu protected override string BeatmapFile => "circles.osz"; - public const double TRACK_START_DELAY = 600; + public const double TRACK_START_DELAY = 1200; private const double delay_for_menu = 2900; From 5a4196fd51f4631ee60a94e856fa82bc9bdcdc20 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Mon, 10 Oct 2022 15:34:29 +0200 Subject: [PATCH 261/295] Make `currentEffectPoint` non-nullable --- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index 9105a78741..c2709db747 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint currentTimingPoint = timingPoints[i]; - EffectControlPoint? currentEffectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTimingPoint.Time); + EffectControlPoint currentEffectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTimingPoint.Time); int currentBeat = 0; // Stop on the next timing point, or if there is no next timing point stop slightly past the last object @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Objects double startTime = currentTimingPoint.Time; double barLength = currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; - if (currentEffectPoint != null && currentEffectPoint.OmitFirstBarLine) + if (currentEffectPoint.OmitFirstBarLine) { startTime += barLength; } From be5a413753346fcb330bf81c71b40fe9aec83ce0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 01:35:35 +0900 Subject: [PATCH 262/295] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2a678f1c61..f71adff23b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index efdb6c6995..cb414ebe31 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 47872e4ff7..32e22d6c6f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + @@ -82,7 +82,7 @@ - + From d700040a0d5da69a5d8e8b7396abc398b90e62fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 01:38:37 +0900 Subject: [PATCH 263/295] Make country code parsing resilient to invalid cases --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 2 +- .../Containers/Markdown/OsuMarkdownTextFlowContainer.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 31a866b8f5..863b352618 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -195,7 +195,7 @@ Line after image"; AddStep("Add flag", () => { markdownContainer.CurrentPath = @"https://dev.ppy.sh"; - markdownContainer.Text = "::{flag=\"AU\"}::"; + markdownContainer.Text = "::{flag=\"AU\"}:: ::{flag=\"ZZ\"}::"; }); } diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index fb8f13ab84..9d7b47281f 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -57,7 +57,11 @@ namespace osu.Game.Graphics.Containers.Markdown } string flag = flagAttribute.Split('=').Last().Trim('"'); - AddDrawable(new DrawableFlag(Enum.Parse(flag)) { Size = new Vector2(20, 15) }); + + if (!Enum.TryParse(flag, out var countryCode)) + countryCode = CountryCode.Unknown; + + AddDrawable(new DrawableFlag(countryCode) { Size = new Vector2(20, 15) }); } private class OsuMarkdownInlineCode : Container From 32adc1ec14a558950f0e78e7aec2f6ffda540c48 Mon Sep 17 00:00:00 2001 From: Natelytle Date: Mon, 10 Oct 2022 13:36:52 -0400 Subject: [PATCH 264/295] Fix division by 0 --- .../Difficulty/ManiaPerformanceCalculator.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index a925e7c0ac..5d7fec3ede 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty countOk = score.Statistics.GetValueOrDefault(HitResult.Ok); countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); - scoreAccuracy = customAccuracy; + scoreAccuracy = customAccuracy(); // Arbitrary initial value for scaling pp in order to standardize distributions across game modes. // The specific number has no intrinsic meaning and can be adjusted as needed. @@ -73,6 +73,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty /// /// Accuracy used to weight judgements independently from the score's actual accuracy. /// - private double customAccuracy => (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320); + private double customAccuracy() + { + if (totalHits==0) + return 0; + + return (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320); + } } } From 430eb44446e22a2e2cca0b9a7f9ab317a6f65c48 Mon Sep 17 00:00:00 2001 From: Natelytle Date: Mon, 10 Oct 2022 13:45:16 -0400 Subject: [PATCH 265/295] slight formatting --- .../Difficulty/ManiaPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 5d7fec3ede..5e10e7a8ec 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -75,8 +75,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty /// private double customAccuracy() { - if (totalHits==0) - return 0; + if (totalHits == 0) + return 0; return (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320); } From 3cd6ce8e3f2ef06f94e9a2711cb7e04cff816f48 Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Tue, 11 Oct 2022 01:48:55 +0800 Subject: [PATCH 266/295] Update WorkingBeatmap.cs --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8bd0f4b14b..7eb7852897 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -110,7 +110,7 @@ namespace osu.Game.Beatmaps public Track LoadTrack() => track = GetBeatmapTrack() ?? GetVirtualTrack(1000); - public void PrepareTrackForPreview(bool looping, int priorToPreview = 2000) + public void PrepareTrackForPreview(bool looping, int priorToPreview = 0) { Track.Looping = looping; Track.RestartPoint = Metadata.PreviewTime - priorToPreview; From 8a4f2efa141c47a54dcfb12ae4047d508e7659f4 Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Tue, 11 Oct 2022 01:49:49 +0800 Subject: [PATCH 267/295] Update IntroScreen.cs --- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 409c7d6c8d..42a993ff63 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -278,7 +278,7 @@ namespace osu.Game.Screens.Menu if (!UsingThemedIntro) { - initialBeatmap?.PrepareTrackForPreview(false); + initialBeatmap?.PrepareTrackForPreview(false, 2200); drawableTrack.VolumeTo(0); drawableTrack.Restart(); From ef840e98cf5c75b56d051c489a3179ebbf34c3c2 Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Tue, 11 Oct 2022 01:49:53 +0800 Subject: [PATCH 268/295] Update IntroCircles.cs --- osu.Game/Screens/Menu/IntroCircles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroCircles.cs b/osu.Game/Screens/Menu/IntroCircles.cs index a69f2c8a65..97654a81df 100644 --- a/osu.Game/Screens/Menu/IntroCircles.cs +++ b/osu.Game/Screens/Menu/IntroCircles.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Menu protected override string BeatmapFile => "circles.osz"; - public const double TRACK_START_DELAY = 1200; + public const double TRACK_START_DELAY = 1000; private const double delay_for_menu = 2900; From cc54fc5c1b8d491203e19cbd3e1f05034e121691 Mon Sep 17 00:00:00 2001 From: Natelytle Date: Mon, 10 Oct 2022 13:52:01 -0400 Subject: [PATCH 269/295] rename method to reflect being a method --- .../Difficulty/ManiaPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 5e10e7a8ec..d6c2dc1d9e 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty countOk = score.Statistics.GetValueOrDefault(HitResult.Ok); countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss); - scoreAccuracy = customAccuracy(); + scoreAccuracy = calculateCustomAccuracy(); // Arbitrary initial value for scaling pp in order to standardize distributions across game modes. // The specific number has no intrinsic meaning and can be adjusted as needed. @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty /// /// Accuracy used to weight judgements independently from the score's actual accuracy. /// - private double customAccuracy() + private double calculateCustomAccuracy() { if (totalHits == 0) return 0; From de8d4853055c31d43e435a6093a9af00a6c72b24 Mon Sep 17 00:00:00 2001 From: pfg Date: Mon, 10 Oct 2022 21:45:33 -0400 Subject: [PATCH 270/295] Move 'transferCollections' to a shared location --- osu.Game/Beatmaps/BeatmapImporter.cs | 8 +------- osu.Game/Beatmaps/BeatmapInfo.cs | 12 ++++++++++++ osu.Game/Beatmaps/BeatmapManager.cs | 6 +----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 292caa4397..c4378cda5c 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -146,13 +146,7 @@ namespace osu.Game.Beatmaps if (updatedBeatmap == null) continue; - var collections = realm.All().AsEnumerable().Where(c => c.BeatmapMD5Hashes.Contains(originalBeatmap.MD5Hash)); - - foreach (var c in collections) - { - c.BeatmapMD5Hashes.Remove(originalBeatmap.MD5Hash); - c.BeatmapMD5Hashes.Add(updatedBeatmap.MD5Hash); - } + updatedBeatmap.transferCollectionsFrom(realm, originalBeatmap.MD5Hash); } } diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 32b7f0b29b..61431764f0 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -8,6 +8,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Collections; using osu.Game.Database; using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; @@ -213,6 +214,17 @@ namespace osu.Game.Beatmaps return fileHashX == fileHashY; } + public void transferCollectionsFrom(Realm realm, string oldMd5Hash) + { + var collections = realm.All().AsEnumerable().Where(c => c.BeatmapMD5Hashes.Contains(oldMd5Hash)); + + foreach (var c in collections) + { + c.BeatmapMD5Hashes.Remove(oldMd5Hash); + c.BeatmapMD5Hashes.Add(MD5Hash); + } + } + IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata; IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet; IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 71cafd46c0..7db5480128 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -330,11 +330,7 @@ namespace osu.Game.Beatmaps setInfo.CopyChangesToRealm(liveBeatmapSet); - foreach (var collection in r.All()) - { - if (collection.BeatmapMD5Hashes.Remove(oldMd5Hash)) - collection.BeatmapMD5Hashes.Add(beatmapInfo.MD5Hash); - } + beatmapInfo.transferCollectionsFrom(r, oldMd5Hash); ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); From e619c9c1e6eb1c60302158b21ad0d5b7ff5328af Mon Sep 17 00:00:00 2001 From: pfg Date: Mon, 10 Oct 2022 21:48:41 -0400 Subject: [PATCH 271/295] fix style --- osu.Game/Beatmaps/BeatmapImporter.cs | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index c4378cda5c..4f09a73440 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -146,7 +146,7 @@ namespace osu.Game.Beatmaps if (updatedBeatmap == null) continue; - updatedBeatmap.transferCollectionsFrom(realm, originalBeatmap.MD5Hash); + updatedBeatmap.TransferCollectionsFrom(realm, originalBeatmap.MD5Hash); } } diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 61431764f0..42461e863c 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -214,7 +214,7 @@ namespace osu.Game.Beatmaps return fileHashX == fileHashY; } - public void transferCollectionsFrom(Realm realm, string oldMd5Hash) + public void TransferCollectionsFrom(Realm realm, string oldMd5Hash) { var collections = realm.All().AsEnumerable().Where(c => c.BeatmapMD5Hashes.Contains(oldMd5Hash)); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 7db5480128..9224806301 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -330,7 +330,7 @@ namespace osu.Game.Beatmaps setInfo.CopyChangesToRealm(liveBeatmapSet); - beatmapInfo.transferCollectionsFrom(r, oldMd5Hash); + beatmapInfo.TransferCollectionsFrom(r, oldMd5Hash); ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); From ed2b39a22012656d03f3238f4f0c422f9fed5af8 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Oct 2022 13:53:18 +0900 Subject: [PATCH 272/295] Remove whitespace --- .../Difficulty/ManiaPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index d6c2dc1d9e..440dec82af 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -79,6 +79,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty return 0; return (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320); - } + } } } From 1a24762f9b75582c0dd0bee7368835c9e04f0730 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 11 Oct 2022 14:11:45 +0900 Subject: [PATCH 273/295] Improve drag box selection logic `AllowDeselectionDuringDrag` is remove. Instead, selected hit objects are not automatically deselected when clock is seeked to a later time (the hit object is dead). Update drag box selection even if mouse is not moved (in case clock is running or scroll wheel is used). --- .../Compose/Components/BlueprintContainer.cs | 23 +++++++++++-------- .../Components/ComposeBlueprintContainer.cs | 20 ++++++++++++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 8aecc75824..b15cecd506 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -106,11 +107,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected virtual DragBox CreateDragBox() => new DragBox(); - /// - /// Whether this component is in a state where items outside a drag selection should be deselected. If false, selection will only be added to. - /// - protected virtual bool AllowDeselectionDuringDrag => true; - protected override bool OnMouseDown(MouseDownEvent e) { bool selectionPerformed = performMouseDownActions(e); @@ -389,12 +385,19 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (var blueprint in SelectionBlueprints) { - if (blueprint.IsSelected && !AllowDeselectionDuringDrag) - continue; + switch (blueprint.State) + { + case SelectionState.Selected: + // Selection is preserved even after blueprint becomes dead. + if (!quad.Contains(blueprint.ScreenSpaceSelectionPoint)) + blueprint.Deselect(); + break; - bool shouldBeSelected = blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint); - if (blueprint.IsSelected != shouldBeSelected) - blueprint.ToggleSelection(); + case SelectionState.NotSelected: + if (blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint)) + blueprint.Select(); + break; + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 43ead88d54..c8870d46a8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -39,6 +39,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private PlacementBlueprint currentPlacement; private InputManager inputManager; + private DragEvent lastDragEvent; + /// /// Positional input must be received outside the container's bounds, /// in order to handle composer blueprints which are partially offscreen. @@ -83,8 +85,6 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - protected override bool AllowDeselectionDuringDrag => !EditorClock.IsRunning; - protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) { base.TransferBlueprintFor(hitObject, drawableObject); @@ -120,6 +120,18 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } + protected override void OnDrag(DragEvent e) + { + base.OnDrag(e); + lastDragEvent = e; + } + + protected override void OnDragEnd(DragEndEvent e) + { + base.OnDragEnd(e); + lastDragEvent = null; + } + /// /// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints). /// @@ -236,6 +248,10 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.Update(); + // trigger every frame so drags continue to update selection while seeking time. + if (lastDragEvent != null) + OnDrag(lastDragEvent); + if (currentPlacement != null) { switch (currentPlacement.PlacementActive) From 6ab29a62d04b66130928cdb463b7b48424025fb5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 11 Oct 2022 14:23:17 +0900 Subject: [PATCH 274/295] Anchor drag box on time (catch, mania) Not done for taiko because I cannot figure out how it should work with the overlapping scroll algorithm. --- .../Edit/CatchBlueprintContainer.cs | 3 + .../Edit/ManiaBlueprintContainer.cs | 3 + .../Compose/Components/ScrollingDragBox.cs | 63 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs b/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs index 58f493b4b8..20d432d62e 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs @@ -5,6 +5,7 @@ using osu.Game.Rulesets.Catch.Edit.Blueprints; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -36,5 +37,7 @@ namespace osu.Game.Rulesets.Catch.Edit return base.CreateHitObjectBlueprintFor(hitObject); } + + protected sealed override DragBox CreateDragBox() => new ScrollingDragBox((CatchPlayfield)Composer.Playfield); } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs index ad75afff8e..c3fad1e22b 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs @@ -6,6 +6,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -33,5 +34,7 @@ namespace osu.Game.Rulesets.Mania.Edit } protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler(); + + protected sealed override DragBox CreateDragBox() => new ScrollingDragBox((ManiaPlayfield)Composer.Playfield); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs b/osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs new file mode 100644 index 0000000000..2d2c88247b --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs @@ -0,0 +1,63 @@ +// 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.Input.Events; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + /// + /// A that scrolls along with the scrolling playfield. + /// + public class ScrollingDragBox : DragBox + { + public double MinTime { get; private set; } + + public double MaxTime { get; private set; } + + private double? startTime; + + private readonly ScrollingPlayfield playfield; + + public ScrollingDragBox(ScrollingPlayfield playfield) + { + this.playfield = playfield; + } + + public override void HandleDrag(MouseButtonEvent e) + { + base.HandleDrag(e); + + startTime ??= playfield.TimeAtScreenSpacePosition(e.ScreenSpaceMouseDownPosition); + double endTime = playfield.TimeAtScreenSpacePosition(e.ScreenSpaceMousePosition); + + MinTime = Math.Min(startTime.Value, endTime); + MaxTime = Math.Max(startTime.Value, endTime); + + var startPos = ToLocalSpace(playfield.ScreenSpacePositionAtTime(startTime.Value)); + var endPos = ToLocalSpace(playfield.ScreenSpacePositionAtTime(endTime)); + + switch (playfield.ScrollingInfo.Direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + Box.Y = Math.Min(startPos.Y, endPos.Y); + Box.Height = Math.Max(startPos.Y, endPos.Y) - Box.Y; + break; + + case ScrollingDirection.Left: + case ScrollingDirection.Right: + Box.X = Math.Min(startPos.X, endPos.X); + Box.Width = Math.Max(startPos.X, endPos.X) - Box.X; + break; + } + } + + public override void Hide() + { + base.Hide(); + startTime = null; + } + } +} From a44ba579c5c39f8878e3f93f51614a270a13fc66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 11:29:17 +0900 Subject: [PATCH 275/295] Fix beatmap update button not respecting user "prefer no video" setting Closes #20701. --- osu.Game/Database/ModelDownloader.cs | 2 +- .../Screens/Select/Carousel/UpdateBeatmapSetButton.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs index 877c90a534..6cba8fe819 100644 --- a/osu.Game/Database/ModelDownloader.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -45,7 +45,7 @@ namespace osu.Game.Database public bool Download(T model, bool minimiseDownloadSize = false) => Download(model, minimiseDownloadSize, null); - public void DownloadAsUpdate(TModel originalModel) => Download(originalModel, false, originalModel); + public void DownloadAsUpdate(TModel originalModel, bool minimiseDownloadSize) => Download(originalModel, minimiseDownloadSize, originalModel); protected bool Download(T model, bool minimiseDownloadSize, TModel? originalModel) { diff --git a/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs b/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs index 3c4ed4734b..023d3627b0 100644 --- a/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs +++ b/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs @@ -2,12 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -43,11 +45,15 @@ namespace osu.Game.Screens.Select.Carousel Origin = Anchor.CentreLeft; } + private Bindable preferNoVideo = null!; + [BackgroundDependencyLoader] - private void load() + private void load(OsuConfigManager config) { const float icon_size = 14; + preferNoVideo = config.GetBindable(OsuSetting.PreferNoVideo); + Content.Anchor = Anchor.CentreLeft; Content.Origin = Anchor.CentreLeft; @@ -104,7 +110,7 @@ namespace osu.Game.Screens.Select.Carousel return; } - beatmapDownloader.DownloadAsUpdate(beatmapSetInfo); + beatmapDownloader.DownloadAsUpdate(beatmapSetInfo, preferNoVideo.Value); attachExistingDownload(); }; } From 6f6290a21a51477dfb1546d0305afcb7b8ea5c07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 15:37:22 +0900 Subject: [PATCH 276/295] Use async flow for storing key binding changes to realm --- .../Overlays/Settings/Sections/Input/KeyBindingRow.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 9ff47578e9..2fea2e34b2 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -387,14 +387,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (bindTarget != null) bindTarget.IsBinding = true; } - private void updateStoreFromButton(KeyButton button) - { - realm.Run(r => - { - var binding = r.Find(((IHasGuidPrimaryKey)button.KeyBinding).ID); - r.Write(() => binding.KeyCombinationString = button.KeyBinding.KeyCombinationString); - }); - } + private void updateStoreFromButton(KeyButton button) => + realm.WriteAsync(r => r.Find(button.KeyBinding.ID).KeyCombinationString = button.KeyBinding.KeyCombinationString); private void updateIsDefaultValue() { From eba3d37a1197e0a4accfac3d69c1330d316f351c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 15:50:02 +0900 Subject: [PATCH 277/295] Add xmldoc and simplify calling --- osu.Game/Beatmaps/BeatmapImporter.cs | 9 +++------ osu.Game/Beatmaps/BeatmapInfo.cs | 12 +++++++++--- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 4f09a73440..bcb1d7f961 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -141,12 +141,9 @@ namespace osu.Game.Beatmaps // Handle collections using permissive difficulty name to track difficulties. foreach (var originalBeatmap in original.Beatmaps) { - var updatedBeatmap = updated.Beatmaps.FirstOrDefault(b => b.DifficultyName == originalBeatmap.DifficultyName); - - if (updatedBeatmap == null) - continue; - - updatedBeatmap.TransferCollectionsFrom(realm, originalBeatmap.MD5Hash); + updated.Beatmaps + .FirstOrDefault(b => b.DifficultyName == originalBeatmap.DifficultyName)? + .TransferCollectionReferences(realm, originalBeatmap.MD5Hash); } } diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 42461e863c..6f9df1ba7f 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -214,13 +214,19 @@ namespace osu.Game.Beatmaps return fileHashX == fileHashY; } - public void TransferCollectionsFrom(Realm realm, string oldMd5Hash) + /// + /// When updating a beatmap, its hashes will change. Collections currently track beatmaps by hash, so they need to be updated. + /// This method will handle updating + /// + /// A realm instance in an active write transaction. + /// The previous MD5 hash of the beatmap before update. + public void TransferCollectionReferences(Realm realm, string previousMD5Hash) { - var collections = realm.All().AsEnumerable().Where(c => c.BeatmapMD5Hashes.Contains(oldMd5Hash)); + var collections = realm.All().AsEnumerable().Where(c => c.BeatmapMD5Hashes.Contains(previousMD5Hash)); foreach (var c in collections) { - c.BeatmapMD5Hashes.Remove(oldMd5Hash); + c.BeatmapMD5Hashes.Remove(previousMD5Hash); c.BeatmapMD5Hashes.Add(MD5Hash); } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 9224806301..8ea22e5d67 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -330,7 +330,7 @@ namespace osu.Game.Beatmaps setInfo.CopyChangesToRealm(liveBeatmapSet); - beatmapInfo.TransferCollectionsFrom(r, oldMd5Hash); + beatmapInfo.TransferCollectionReferences(r, oldMd5Hash); ProcessBeatmap?.Invoke((liveBeatmapSet, false)); }); From ece42de408544565821121591a953813a6fba5aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 15:53:48 +0900 Subject: [PATCH 278/295] Fix variable naming in test --- .../Beatmaps/WorkingBeatmapManagerTest.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs index 006326fd3c..4ef466efbf 100644 --- a/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs +++ b/osu.Game.Tests/Beatmaps/WorkingBeatmapManagerTest.cs @@ -104,15 +104,17 @@ namespace osu.Game.Tests.Beatmaps { var beatmap = Realm.Run(r => r.Find(importedSet.Beatmaps.First().ID).Detach()); - var map = beatmaps.GetWorkingBeatmap(beatmap); + var working = beatmaps.GetWorkingBeatmap(beatmap); - Assert.That(map.BeatmapInfo.BeatmapSet?.Files, Has.Count.GreaterThan(0)); + Assert.That(working.BeatmapInfo.BeatmapSet?.Files, Has.Count.GreaterThan(0)); - string initialHash = map.BeatmapInfo.MD5Hash; + string initialHash = working.BeatmapInfo.MD5Hash; - var preserveCollection = new BeatmapCollection("test save preserves collection"); + var preserveCollection = new BeatmapCollection("test contained"); preserveCollection.BeatmapMD5Hashes.Add(initialHash); - var noNewCollection = new BeatmapCollection("test save does not introduce new collection"); + + var noNewCollection = new BeatmapCollection("test not contained"); + Realm.Write(r => { r.Add(preserveCollection); @@ -122,9 +124,9 @@ namespace osu.Game.Tests.Beatmaps Assert.That(preserveCollection.BeatmapMD5Hashes, Does.Contain(initialHash)); Assert.That(noNewCollection.BeatmapMD5Hashes, Does.Not.Contain(initialHash)); - beatmaps.Save(map.BeatmapInfo, map.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + beatmaps.Save(working.BeatmapInfo, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); - string finalHash = map.BeatmapInfo.MD5Hash; + string finalHash = working.BeatmapInfo.MD5Hash; Assert.That(finalHash, Is.Not.SameAs(initialHash)); From 9bab02fab4a4fc9df3c6037394f2a8fba5f1edf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 16:23:05 +0900 Subject: [PATCH 279/295] Remove unused using statement --- osu.Game/Beatmaps/BeatmapManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 8ea22e5d67..befc56d244 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -17,7 +17,6 @@ using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps.Formats; -using osu.Game.Collections; using osu.Game.Database; using osu.Game.Extensions; using osu.Game.IO.Archives; From f060e6a780c43057eea6cab2e140c8b6ca8da26e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 16:31:37 +0900 Subject: [PATCH 280/295] Implement hold "sliding" samples in osu!mania --- .../Objects/Drawables/DrawableHoldNote.cs | 45 +++++++++++++++++++ osu.Game.Rulesets.Osu/Objects/Slider.cs | 15 ------- osu.Game/Rulesets/Objects/HitObject.cs | 16 +++++++ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 19792086a7..48647f9f5f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -4,12 +4,14 @@ #nullable disable using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Audio; using osu.Game.Rulesets.Mania.Skinning.Default; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -38,6 +40,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables private Container tailContainer; private Container tickContainer; + private PausableSkinnableSound slidingSample; + /// /// Contains the size of the hold note covering the whole head/tail bounds. The size of this container changes as the hold note is being pressed. /// @@ -108,6 +112,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables }, tickContainer = new Container { RelativeSizeAxes = Axes.Both }, tailContainer = new Container { RelativeSizeAxes = Axes.Both }, + slidingSample = new PausableSkinnableSound { Looping = true } }); maskedContents.AddRange(new[] @@ -118,6 +123,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables }); } + protected override void LoadComplete() + { + base.LoadComplete(); + + isHitting.BindValueChanged(updateSlidingSample, true); + } + protected override void OnApply() { base.OnApply(); @@ -322,5 +334,38 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables HoldStartTime = null; isHitting.Value = false; } + + protected override void LoadSamples() + { + // Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being. + + if (HitObject.SampleControlPoint == null) + { + throw new InvalidOperationException($"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." + + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); + } + + slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast().ToArray(); + } + + public override void StopAllSamples() + { + base.StopAllSamples(); + slidingSample?.Stop(); + } + + private void updateSlidingSample(ValueChangedEvent tracking) + { + if (tracking.NewValue) + slidingSample?.Play(); + else + slidingSample?.Stop(); + } + + protected override void OnFree() + { + slidingSample.Samples = null; + base.OnFree(); + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index e3c1b1e168..6c2be8a49a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -34,21 +34,6 @@ namespace osu.Game.Rulesets.Osu.Objects public override IList AuxiliarySamples => CreateSlidingSamples().Concat(TailSamples).ToArray(); - public IList CreateSlidingSamples() - { - var slidingSamples = new List(); - - var normalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL); - if (normalSample != null) - slidingSamples.Add(normalSample.With("sliderslide")); - - var whistleSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE); - if (whistleSample != null) - slidingSamples.Add(whistleSample.With("sliderwhistle")); - - return slidingSamples; - } - private readonly Cached endPositionCache = new Cached(); public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1); diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index d20e0616e5..0f79e58201 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using JetBrains.Annotations; using Newtonsoft.Json; @@ -198,6 +199,21 @@ namespace osu.Game.Rulesets.Objects /// [NotNull] protected virtual HitWindows CreateHitWindows() => new HitWindows(); + + public IList CreateSlidingSamples() + { + var slidingSamples = new List(); + + var normalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL); + if (normalSample != null) + slidingSamples.Add(normalSample.With("sliderslide")); + + var whistleSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE); + if (whistleSample != null) + slidingSamples.Add(whistleSample.With("sliderwhistle")); + + return slidingSamples; + } } public static class HitObjectExtensions From f41b79688f184ab9182ef4ffde6e7ce8bd797bfc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 16:54:41 +0900 Subject: [PATCH 281/295] Avoid casting by accepting all `Playfield`s but throwing on a bad choice --- osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs | 3 +-- osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs | 3 +-- osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs | 5 +++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs b/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs index 20d432d62e..a0a11424d0 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs @@ -5,7 +5,6 @@ using osu.Game.Rulesets.Catch.Edit.Blueprints; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -38,6 +37,6 @@ namespace osu.Game.Rulesets.Catch.Edit return base.CreateHitObjectBlueprintFor(hitObject); } - protected sealed override DragBox CreateDragBox() => new ScrollingDragBox((CatchPlayfield)Composer.Playfield); + protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield); } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs index c3fad1e22b..f438d6497c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs @@ -6,7 +6,6 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -35,6 +34,6 @@ namespace osu.Game.Rulesets.Mania.Edit protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler(); - protected sealed override DragBox CreateDragBox() => new ScrollingDragBox((ManiaPlayfield)Composer.Playfield); + protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs b/osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs index 2d2c88247b..58bfaf56ff 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Input.Events; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Screens.Edit.Compose.Components @@ -20,9 +21,9 @@ namespace osu.Game.Screens.Edit.Compose.Components private readonly ScrollingPlayfield playfield; - public ScrollingDragBox(ScrollingPlayfield playfield) + public ScrollingDragBox(Playfield playfield) { - this.playfield = playfield; + this.playfield = playfield as ScrollingPlayfield ?? throw new ArgumentException("Playfield must be of type {nameof(ScrollingPlayfield)} to use this class.", nameof(playfield)); } public override void HandleDrag(MouseButtonEvent e) From 5baad52a9ef8ab064633bc4a76f3eced160a246c Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Tue, 11 Oct 2022 16:15:56 +0800 Subject: [PATCH 282/295] Fix unintended delay on osu!theme --- osu.Game/Screens/Menu/IntroCircles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroCircles.cs b/osu.Game/Screens/Menu/IntroCircles.cs index 97654a81df..7a4bdb231f 100644 --- a/osu.Game/Screens/Menu/IntroCircles.cs +++ b/osu.Game/Screens/Menu/IntroCircles.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Menu protected override string BeatmapFile => "circles.osz"; - public const double TRACK_START_DELAY = 1000; + public const double TRACK_START_DELAY = 600; private const double delay_for_menu = 2900; From d18466d7406244f1f76f4c83f5a417c869eb26ce Mon Sep 17 00:00:00 2001 From: NotGumballer91 <64581009+NotGumballer91@users.noreply.github.com> Date: Tue, 11 Oct 2022 16:16:57 +0800 Subject: [PATCH 283/295] Fix delay inconsistency in IntroCircles.cs --- osu.Game/Screens/Menu/IntroScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 42a993ff63..435c02de96 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -278,11 +278,11 @@ namespace osu.Game.Screens.Menu if (!UsingThemedIntro) { - initialBeatmap?.PrepareTrackForPreview(false, 2200); + initialBeatmap?.PrepareTrackForPreview(false, 2600); drawableTrack.VolumeTo(0); drawableTrack.Restart(); - drawableTrack.VolumeTo(1, 2200, Easing.InCubic); + drawableTrack.VolumeTo(1, 2600, Easing.InCubic); } else { From 390ff8b9da93841a0dc78577f50959158d9a5bed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Oct 2022 17:33:44 +0900 Subject: [PATCH 284/295] Reduce the scope of realm transactions during import operations --- .../Database/RealmArchiveModelImporter.cs | 45 ++++++++++++------- osu.Game/Database/RealmFileStore.cs | 6 +-- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/osu.Game/Database/RealmArchiveModelImporter.cs b/osu.Game/Database/RealmArchiveModelImporter.cs index f1bc0bfe0e..0286815569 100644 --- a/osu.Game/Database/RealmArchiveModelImporter.cs +++ b/osu.Game/Database/RealmArchiveModelImporter.cs @@ -294,15 +294,38 @@ namespace osu.Game.Database // Log output here will be missing a valid hash in non-batch imports. LogForModel(item, $@"Beginning import from {archive?.Name ?? "unknown"}..."); + List files = new List(); + + if (archive != null) + { + // Import files to the disk store. + // We intentionally delay adding to realm to avoid blocking on a write during disk operations. + foreach (var filenames in getShortenedFilenames(archive)) + { + using (Stream s = archive.GetStream(filenames.original)) + files.Add(new RealmNamedFileUsage(Files.Add(s, realm, false), filenames.shortened)); + } + } + + using (var transaction = realm.BeginWrite()) + { + // Add all files to realm in one go. + // This is done ahead of the main transaction to ensure we can correctly cleanup the files, even if the import fails. + foreach (var file in files) + { + if (!file.File.IsManaged) + realm.Add(file.File, true); + } + + transaction.Commit(); + } + + item.Files.AddRange(files); + item.Hash = ComputeHash(item); + // TODO: do we want to make the transaction this local? not 100% sure, will need further investigation. using (var transaction = realm.BeginWrite()) { - if (archive != null) - // TODO: look into rollback of file additions (or delayed commit). - item.Files.AddRange(createFileInfos(archive, Files, realm)); - - item.Hash = ComputeHash(item); - // TODO: we may want to run this outside of the transaction. Populate(item, archive, realm, cancellationToken); @@ -425,16 +448,6 @@ namespace osu.Game.Database { var fileInfos = new List(); - // import files to manager - foreach (var filenames in getShortenedFilenames(reader)) - { - using (Stream s = reader.GetStream(filenames.original)) - { - var item = new RealmNamedFileUsage(files.Add(s, realm), filenames.shortened); - fileInfos.Add(item); - } - } - return fileInfos; } diff --git a/osu.Game/Database/RealmFileStore.cs b/osu.Game/Database/RealmFileStore.cs index ecb152c45c..036b15ea17 100644 --- a/osu.Game/Database/RealmFileStore.cs +++ b/osu.Game/Database/RealmFileStore.cs @@ -40,8 +40,8 @@ namespace osu.Game.Database /// /// The file data stream. /// The realm instance to add to. Should already be in a transaction. - /// - public RealmFile Add(Stream data, Realm realm) + /// Whether the should immediately be added to the underlying realm. If false is provided here, the instance must be manually added. + public RealmFile Add(Stream data, Realm realm, bool addToRealm = true) { string hash = data.ComputeSHA2Hash(); @@ -52,7 +52,7 @@ namespace osu.Game.Database if (!checkFileExistsAndMatchesHash(file)) copyToStore(file, data); - if (!file.IsManaged) + if (addToRealm && !file.IsManaged) realm.Add(file); return file; From fcb9e2cc00894cf686a9fb73cbe27fd8af6dc766 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 11 Oct 2022 22:39:53 +0900 Subject: [PATCH 285/295] Refactor blueprint container drag code --- .../Compose/Components/BlueprintContainer.cs | 28 +++++++---- .../Components/ComposeBlueprintContainer.cs | 24 +--------- .../Components/EditorBlueprintContainer.cs | 5 ++ .../Timeline/TimelineBlueprintContainer.cs | 47 ++++++------------- 4 files changed, 38 insertions(+), 66 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index b15cecd506..43ad270c16 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -170,11 +170,15 @@ namespace osu.Game.Screens.Edit.Compose.Components finishSelectionMovement(); } + private MouseButtonEvent lastDragEvent; + protected override bool OnDragStart(DragStartEvent e) { if (e.Button == MouseButton.Right) return false; + lastDragEvent = e; + if (movementBlueprints != null) { isDraggingBlueprint = true; @@ -189,22 +193,14 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void OnDrag(DragEvent e) { - if (e.Button == MouseButton.Right) - return; - - if (DragBox.State == Visibility.Visible) - { - DragBox.HandleDrag(e); - UpdateSelectionFromDragBox(); - } + lastDragEvent = e; moveCurrentSelection(e); } protected override void OnDragEnd(DragEndEvent e) { - if (e.Button == MouseButton.Right) - return; + lastDragEvent = null; if (isDraggingBlueprint) { @@ -215,6 +211,18 @@ namespace osu.Game.Screens.Edit.Compose.Components DragBox.Hide(); } + protected override void Update() + { + base.Update(); + + if (lastDragEvent != null && DragBox.State == Visibility.Visible) + { + lastDragEvent.Target = this; + DragBox.HandleDrag(lastDragEvent); + UpdateSelectionFromDragBox(); + } + } + /// /// Called whenever a drag operation completes, before any change transaction is committed. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index c8870d46a8..ec07da43a0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -12,7 +12,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Graphics.UserInterface; @@ -37,9 +36,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected new EditorSelectionHandler SelectionHandler => (EditorSelectionHandler)base.SelectionHandler; private PlacementBlueprint currentPlacement; - private InputManager inputManager; - - private DragEvent lastDragEvent; /// /// Positional input must be received outside the container's bounds, @@ -68,8 +64,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); - inputManager = GetContainingInputManager(); - Beatmap.HitObjectAdded += hitObjectAdded; // updates to selected are handled for us by SelectionHandler. @@ -120,18 +114,6 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - protected override void OnDrag(DragEvent e) - { - base.OnDrag(e); - lastDragEvent = e; - } - - protected override void OnDragEnd(DragEndEvent e) - { - base.OnDragEnd(e); - lastDragEvent = null; - } - /// /// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints). /// @@ -234,7 +216,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updatePlacementPosition() { - var snapResult = Composer.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position); + var snapResult = Composer.FindSnappedPositionAndTime(InputManager.CurrentState.Mouse.Position); // if no time was found from positional snapping, we should still quantize to the beat. snapResult.Time ??= Beatmap.SnapTime(EditorClock.CurrentTime, null); @@ -248,10 +230,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.Update(); - // trigger every frame so drags continue to update selection while seeking time. - if (lastDragEvent != null) - OnDrag(lastDragEvent); - if (currentPlacement != null) { switch (currentPlacement.PlacementActive) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 6adaeb1a83..7423b368b4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -8,6 +8,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -27,6 +28,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private HitObjectUsageEventBuffer usageEventBuffer; + protected InputManager InputManager { get; private set; } + protected EditorBlueprintContainer(HitObjectComposer composer) { Composer = composer; @@ -42,6 +45,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); + InputManager = GetContainingInputManager(); + Beatmap.HitObjectAdded += AddBlueprintFor; Beatmap.HitObjectRemoved += RemoveBlueprintFor; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 31990bfd35..b79c2675c8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -29,10 +29,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved(CanBeNull = true)] private Timeline timeline { get; set; } - private DragEvent lastDragEvent; private Bindable placement; private SelectionBlueprint placementBlueprint; + private bool hitObjectDragged; + /// /// Positional input must be received outside the container's bounds, /// in order to handle timeline blueprints which are stacked offscreen. @@ -98,24 +99,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return base.OnDragStart(e); } - protected override void OnDrag(DragEvent e) - { - handleScrollViaDrag(e); - - base.OnDrag(e); - } - - protected override void OnDragEnd(DragEndEvent e) - { - base.OnDragEnd(e); - lastDragEvent = null; - } - protected override void Update() { - // trigger every frame so drags continue to update selection while playback is scrolling the timeline. - if (lastDragEvent != null) - OnDrag(lastDragEvent); + if (IsDragged || hitObjectDragged) + handleScrollViaDrag(); if (Composer != null && timeline != null) { @@ -170,7 +157,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { return new TimelineHitObjectBlueprint(item) { - OnDragHandled = handleScrollViaDrag, + OnDragHandled = e => hitObjectDragged = e != null, }; } @@ -197,24 +184,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - private void handleScrollViaDrag(DragEvent e) + private void handleScrollViaDrag() { - lastDragEvent = e; + if (timeline == null) return; - if (lastDragEvent == null) - return; + var timelineQuad = timeline.ScreenSpaceDrawQuad; + float mouseX = InputManager.CurrentState.Mouse.Position.X; - if (timeline != null) - { - var timelineQuad = timeline.ScreenSpaceDrawQuad; - float mouseX = e.ScreenSpaceMousePosition.X; - - // scroll if in a drag and dragging outside visible extents - if (mouseX > timelineQuad.TopRight.X) - timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime)); - else if (mouseX < timelineQuad.TopLeft.X) - timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime)); - } + // scroll if in a drag and dragging outside visible extents + if (mouseX > timelineQuad.TopRight.X) + timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime)); + else if (mouseX < timelineQuad.TopLeft.X) + timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime)); } private class SelectableAreaBackground : CompositeDrawable From 84fdd2e107dc3045a295eef5668bea8cd58e24cd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 11 Oct 2022 17:16:57 +0300 Subject: [PATCH 286/295] Improve flashlight display on break periods --- .../Mods/CatchModFlashlight.cs | 6 +-- .../Mods/ManiaModFlashlight.cs | 6 +-- .../Mods/OsuModFlashlight.cs | 6 +-- .../Mods/TaikoModFlashlight.cs | 12 +++--- osu.Game/Rulesets/Mods/ModFlashlight.cs | 38 ++++++++----------- 5 files changed, 31 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index ff957b9b73..a9e9e8fbd5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Mods { this.playfield = playfield; - FlashlightSize = new Vector2(0, GetSizeFor(0)); + FlashlightSize = new Vector2(0, GetSize()); FlashlightSmoothness = 1.4f; } @@ -66,9 +66,9 @@ namespace osu.Game.Rulesets.Catch.Mods FlashlightPosition = playfield.CatcherArea.ToSpaceOfOtherDrawable(playfield.Catcher.DrawPosition, this); } - protected override void OnComboChange(ValueChangedEvent e) + protected override void UpdateFlashlightSize(float size) { - this.TransformTo(nameof(FlashlightSize), new Vector2(0, GetSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION); + this.TransformTo(nameof(FlashlightSize), new Vector2(0, size), FLASHLIGHT_FADE_DURATION); } protected override string FragmentShader => "CircularFlashlight"; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs index 6eaede2112..947915cdf9 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Mods public ManiaFlashlight(ManiaModFlashlight modFlashlight) : base(modFlashlight) { - FlashlightSize = new Vector2(DrawWidth, GetSizeFor(0)); + FlashlightSize = new Vector2(DrawWidth, GetSize()); AddLayout(flashlightProperties); } @@ -54,9 +54,9 @@ namespace osu.Game.Rulesets.Mania.Mods } } - protected override void OnComboChange(ValueChangedEvent e) + protected override void UpdateFlashlightSize(float size) { - this.TransformTo(nameof(FlashlightSize), new Vector2(DrawWidth, GetSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION); + this.TransformTo(nameof(FlashlightSize), new Vector2(DrawWidth, size), FLASHLIGHT_FADE_DURATION); } protected override string FragmentShader => "RectangularFlashlight"; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 66f367c79b..1a86901d9c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Mods { followDelay = modFlashlight.FollowDelay.Value; - FlashlightSize = new Vector2(0, GetSizeFor(0)); + FlashlightSize = new Vector2(0, GetSize()); FlashlightSmoothness = 1.4f; } @@ -83,9 +83,9 @@ namespace osu.Game.Rulesets.Osu.Mods return base.OnMouseMove(e); } - protected override void OnComboChange(ValueChangedEvent e) + protected override void UpdateFlashlightSize(float size) { - this.TransformTo(nameof(FlashlightSize), new Vector2(0, GetSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION); + this.TransformTo(nameof(FlashlightSize), new Vector2(0, size), FLASHLIGHT_FADE_DURATION); } protected override string FragmentShader => "CircularFlashlight"; diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index fca69e86cc..98f954ad29 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -47,21 +47,21 @@ namespace osu.Game.Rulesets.Taiko.Mods { this.taikoPlayfield = taikoPlayfield; - FlashlightSize = getSizeFor(0); + FlashlightSize = adjustSize(GetSize()); FlashlightSmoothness = 1.4f; AddLayout(flashlightProperties); } - private Vector2 getSizeFor(int combo) + private Vector2 adjustSize(float size) { // Preserve flashlight size through the playfield's aspect adjustment. - return new Vector2(0, GetSizeFor(combo) * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT); + return new Vector2(0, size * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT); } - protected override void OnComboChange(ValueChangedEvent e) + protected override void UpdateFlashlightSize(float size) { - this.TransformTo(nameof(FlashlightSize), getSizeFor(e.NewValue), FLASHLIGHT_FADE_DURATION); + this.TransformTo(nameof(FlashlightSize), adjustSize(size), FLASHLIGHT_FADE_DURATION); } protected override string FragmentShader => "CircularFlashlight"; @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Taiko.Mods FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre); ClearTransforms(targetMember: nameof(FlashlightSize)); - FlashlightSize = getSizeFor(Combo.Value); + FlashlightSize = adjustSize(Combo.Value); flashlightProperties.Validate(); } diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 69937a0fba..d58a901154 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -12,7 +11,6 @@ using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.OpenGL.Vertices; @@ -20,6 +18,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osuTK; using osuTK.Graphics; @@ -84,8 +83,6 @@ namespace osu.Game.Rulesets.Mods flashlight.Combo.BindTo(Combo); drawableRuleset.KeyBindingInputManager.Add(flashlight); - - flashlight.Breaks = drawableRuleset.Beatmap.Breaks; } protected abstract Flashlight CreateFlashlight(); @@ -100,8 +97,6 @@ namespace osu.Game.Rulesets.Mods public override bool RemoveCompletedTransforms => false; - public List Breaks = new List(); - private readonly float defaultFlashlightSize; private readonly float sizeMultiplier; private readonly bool comboBasedSize; @@ -119,37 +114,36 @@ namespace osu.Game.Rulesets.Mods shader = shaderManager.Load("PositionAndColour", FragmentShader); } + [Resolved] + private Player? player { get; set; } + + private readonly IBindable isBreakTime = new BindableBool(); + protected override void LoadComplete() { base.LoadComplete(); - Combo.ValueChanged += OnComboChange; + Combo.ValueChanged += _ => UpdateFlashlightSize(GetSize()); - using (BeginAbsoluteSequence(0)) + if (player != null) { - foreach (var breakPeriod in Breaks) - { - if (!breakPeriod.HasEffect) - continue; - - if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue; - - this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION); - this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION); - } + isBreakTime.BindTo(player.IsBreakTime); + isBreakTime.BindValueChanged(_ => UpdateFlashlightSize(GetSize()), true); } } - protected abstract void OnComboChange(ValueChangedEvent e); + protected abstract void UpdateFlashlightSize(float size); protected abstract string FragmentShader { get; } - protected float GetSizeFor(int combo) + protected float GetSize() { float size = defaultFlashlightSize * sizeMultiplier; - if (comboBasedSize) - size *= GetComboScaleFor(combo); + if (isBreakTime.Value) + size *= 2.5f; + else if (comboBasedSize) + size *= GetComboScaleFor(Combo.Value); return size; } From 721bfb5369731496147f4fbe7a278b0a789e8dad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 14:46:35 +0900 Subject: [PATCH 287/295] Rename parameter to read better --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index cf72a611e5..0f0e72b0ac 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -134,6 +134,6 @@ namespace osu.Game.Beatmaps /// /// Reads the correct track restart point from beatmap metadata and sets looping to enabled. /// - void PrepareTrackForPreview(bool looping, int priorToPreview = 0); + void PrepareTrackForPreview(bool looping, double offsetFromPreviewPoint = 0); } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 7eb7852897..d788ae5862 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -110,10 +110,10 @@ namespace osu.Game.Beatmaps public Track LoadTrack() => track = GetBeatmapTrack() ?? GetVirtualTrack(1000); - public void PrepareTrackForPreview(bool looping, int priorToPreview = 0) + public void PrepareTrackForPreview(bool looping, double offsetFromPreviewPoint = 0) { Track.Looping = looping; - Track.RestartPoint = Metadata.PreviewTime - priorToPreview; + Track.RestartPoint = Metadata.PreviewTime - offsetFromPreviewPoint; if (Track.RestartPoint == -1) { From db148d145b4e6d8e5f3484033fbadd867d75fb5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 14:47:15 +0900 Subject: [PATCH 288/295] Fix incorrect offset logic for beatmaps without a preview point specified --- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 +++- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d788ae5862..393c4ba892 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -113,7 +113,7 @@ namespace osu.Game.Beatmaps public void PrepareTrackForPreview(bool looping, double offsetFromPreviewPoint = 0) { Track.Looping = looping; - Track.RestartPoint = Metadata.PreviewTime - offsetFromPreviewPoint; + Track.RestartPoint = Metadata.PreviewTime; if (Track.RestartPoint == -1) { @@ -125,6 +125,8 @@ namespace osu.Game.Beatmaps Track.RestartPoint = 0.4f * Track.Length; } + + Track.RestartPoint += offsetFromPreviewPoint; } /// diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 435c02de96..dcead4a3a8 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -278,7 +278,7 @@ namespace osu.Game.Screens.Menu if (!UsingThemedIntro) { - initialBeatmap?.PrepareTrackForPreview(false, 2600); + initialBeatmap?.PrepareTrackForPreview(false, -2600); drawableTrack.VolumeTo(0); drawableTrack.Restart(); From d6998c810c064ed0b26d3718ac62ffef0d709e77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 14:50:04 +0900 Subject: [PATCH 289/295] Fix incorrect delay for non-theme music on "welcome" intro --- osu.Game/Screens/Menu/IntroWelcome.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroWelcome.cs b/osu.Game/Screens/Menu/IntroWelcome.cs index 9e56a3a0b7..5ae2158172 100644 --- a/osu.Game/Screens/Menu/IntroWelcome.cs +++ b/osu.Game/Screens/Menu/IntroWelcome.cs @@ -78,13 +78,17 @@ namespace osu.Game.Screens.Menu if (reverbChannel != null) intro.LogoVisualisation.AddAmplitudeSource(reverbChannel); - Scheduler.AddDelayed(() => - { + if (!UsingThemedIntro) StartTrack(); - // this classic intro loops forever. + Scheduler.AddDelayed(() => + { if (UsingThemedIntro) + { + StartTrack(); + // this classic intro loops forever. Track.Looping = true; + } const float fade_in_time = 200; From d2d589a15695edab3b1f862b15ef4d27f63808d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 15:11:52 +0900 Subject: [PATCH 290/295] Expose HUD state via `Player` --- osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs | 3 ++- osu.Game/Screens/Play/Player.cs | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs index a923aca2c8..2dc9f5f5cb 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; using osu.Game.Skinning; using osuTK.Graphics; @@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.UI } [BackgroundDependencyLoader(true)] - private void load(DrawableRuleset drawableRuleset) + private void load(DrawableRuleset drawableRuleset, HUDOverlay hud) { hudVisibilityMode = config.GetBindable(OsuSetting.HUDVisibilityMode); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7721d5b912..68b623b781 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -94,6 +94,11 @@ namespace osu.Game.Screens.Play public int RestartCount; + /// + /// Whether the is currently visible. + /// + public IBindable ShowingOverlayComponents = new Bindable(); + [Resolved] private ScoreManager scoreManager { get; set; } @@ -1015,6 +1020,8 @@ namespace osu.Game.Screens.Play }); HUDOverlay.IsPlaying.BindTo(localUserPlaying); + ShowingOverlayComponents.BindTo(HUDOverlay.ShowHud); + DimmableStoryboard.IsBreakTime.BindTo(breakTracker.IsBreakTime); DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); From 652bc4ac61568d9a596bee7944248942416aa590 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 15:14:33 +0900 Subject: [PATCH 291/295] Update `CatchComboDisplay` implementation to use newly exposed bindable --- .../UI/CatchComboDisplay.cs | 64 +++---------------- 1 file changed, 10 insertions(+), 54 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs index 2dc9f5f5cb..86d4e73144 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs @@ -6,11 +6,9 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; using osuTK.Graphics; @@ -27,14 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI [CanBeNull] public ICatchComboCounter ComboCounter => Drawable as ICatchComboCounter; - private Bindable hudVisibilityMode = null!; - - private readonly BindableBool replayLoaded = new BindableBool(); - - private readonly BindableBool showCombo = new BindableBool(); - - [Resolved] - private OsuConfigManager config { get; set; } + private readonly IBindable showCombo = new BindableBool(true); public CatchComboDisplay() : base(new CatchSkinComponent(CatchSkinComponents.CatchComboCounter), _ => Empty()) @@ -42,51 +33,18 @@ namespace osu.Game.Rulesets.Catch.UI } [BackgroundDependencyLoader(true)] - private void load(DrawableRuleset drawableRuleset, HUDOverlay hud) + private void load(Player player) { - hudVisibilityMode = config.GetBindable(OsuSetting.HUDVisibilityMode); - - hudVisibilityMode.BindValueChanged(s => + if (player != null) { - updateVisibilityState(); - }); - - if (drawableRuleset != null) - replayLoaded.BindTo(drawableRuleset.HasReplayLoaded); - - replayLoaded.BindValueChanged(s => - { - updateVisibilityState(); - }); - - showCombo.BindValueChanged(s => - { - if (ComboCounter == null) return; - - if (!s.NewValue) + showCombo.BindTo(player.ShowingOverlayComponents); + showCombo.BindValueChanged(s => { - ComboCounter.Hide(); - } - }); - - updateVisibilityState(); - - void updateVisibilityState() - { - switch (hudVisibilityMode.Value) - { - case HUDVisibilityMode.Never: - showCombo.Value = false; - break; - - case HUDVisibilityMode.HideDuringGameplay: - showCombo.Value = replayLoaded.Value; - break; - - case HUDVisibilityMode.Always: - showCombo.Value = true; - break; - } + if (!s.NewValue) + ComboCounter?.Hide(); + else + ComboCounter?.Show(); + }, true); } } @@ -120,8 +78,6 @@ namespace osu.Game.Rulesets.Catch.UI private void updateCombo(int newCombo, Color4? hitObjectColour) { - if (!showCombo.Value) return; - currentCombo = newCombo; ComboCounter?.UpdateCombo(newCombo, hitObjectColour); } From 2e3e4ac5be0f4e14b2b740a42de9013bcd4a7e4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 15:14:54 +0900 Subject: [PATCH 292/295] Update `CatchComboDisplay` to use NRT --- osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs index 86d4e73144..b2b77b8f11 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs @@ -1,9 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -22,8 +19,7 @@ namespace osu.Game.Rulesets.Catch.UI { private int currentCombo; - [CanBeNull] - public ICatchComboCounter ComboCounter => Drawable as ICatchComboCounter; + public ICatchComboCounter? ComboCounter => Drawable as ICatchComboCounter; private readonly IBindable showCombo = new BindableBool(true); @@ -33,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.UI } [BackgroundDependencyLoader(true)] - private void load(Player player) + private void load(Player? player) { if (player != null) { From ea3d08d5a0160f495e98da0ffdd96b76aaa918fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 15:22:46 +0900 Subject: [PATCH 293/295] Fix collision of external hide with internal logic in `LegacyCatchComboCounter` --- osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs index b2b77b8f11..a804caa542 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -34,13 +35,7 @@ namespace osu.Game.Rulesets.Catch.UI if (player != null) { showCombo.BindTo(player.ShowingOverlayComponents); - showCombo.BindValueChanged(s => - { - if (!s.NewValue) - ComboCounter?.Hide(); - else - ComboCounter?.Show(); - }, true); + showCombo.BindValueChanged(s => this.FadeTo(s.NewValue ? 1 : 0, HUDOverlay.FADE_DURATION, HUDOverlay.FADE_EASING), true); } } From f3e85d2302775bf41401d85a1b1292bcfba738c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 15:25:16 +0900 Subject: [PATCH 294/295] Fix initial state being wrong due to `BindValueChanged` call in BDL load --- osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs index a804caa542..a5b7d8d0af 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchComboDisplay.cs @@ -29,9 +29,13 @@ namespace osu.Game.Rulesets.Catch.UI { } - [BackgroundDependencyLoader(true)] - private void load(Player? player) + [Resolved(canBeNull: true)] + private Player? player { get; set; } + + protected override void LoadComplete() { + base.LoadComplete(); + if (player != null) { showCombo.BindTo(player.ShowingOverlayComponents); From 9d0ae3f0ca956bead9d33821f5b8917af18d1bf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Oct 2022 15:32:24 +0900 Subject: [PATCH 295/295] Update test scene to work with new data source --- .../TestSceneComboCounter.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs index f39af76a2d..f3161f32be 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs @@ -4,14 +4,16 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; -using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; using osuTK; using osuTK.Graphics; @@ -19,16 +21,19 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneComboCounter : CatchSkinnableTestScene { - private OsuConfigManager localConfig = null!; - private ScoreProcessor scoreProcessor = null!; private Color4 judgedObjectColour = Color4.White; + private readonly Bindable showHud = new Bindable(true); + [BackgroundDependencyLoader] private void load() { - Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); + Dependencies.CacheAs(new TestPlayer + { + ShowingOverlayComponents = { BindTarget = showHud }, + }); } [SetUp] @@ -36,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests { scoreProcessor = new ScoreProcessor(new CatchRuleset()); - localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); + showHud.Value = true; SetContents(_ => new CatchComboDisplay { @@ -62,10 +67,10 @@ namespace osu.Game.Rulesets.Catch.Tests ); }); - AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); + AddStep("set hud to never show", () => showHud.Value = false); AddRepeatStep("perform hit", () => performJudgement(HitResult.Great), 5); - AddStep("set hud to show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always)); + AddStep("set hud to show", () => showHud.Value = true); AddRepeatStep("perform hit", () => performJudgement(HitResult.Great), 5); }