diff --git a/osu.Desktop/app.manifest b/osu.Desktop/app.manifest new file mode 100644 index 0000000000..2e9127bf44 --- /dev/null +++ b/osu.Desktop/app.manifest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + true + + + \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 453cf6f94d..01e4ada2f1 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -8,6 +8,7 @@ osu!lazer osu!lazer lazer.ico + app.manifest 0.0.0 0.0.0 diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 479c250eab..f5b7d9166f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -49,7 +49,11 @@ namespace osu.Game.Rulesets.Osu.Skinning return this.GetAnimation(component.LookupName, true, false); case OsuSkinComponents.SliderFollowCircle: - return this.GetAnimation("sliderfollowcircle", true, true); + var followCircle = this.GetAnimation("sliderfollowcircle", true, true); + if (followCircle != null) + // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x + followCircle.Scale *= 0.5f; + return followCircle; case OsuSkinComponents.SliderBall: var sliderBallContent = this.GetAnimation("sliderb", true, true, ""); diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 74ae641bfe..dbea8d28a6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -19,6 +20,7 @@ using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens; @@ -55,6 +57,9 @@ namespace osu.Game.Tests.Visual.Gameplay beforeLoadAction?.Invoke(); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToClock(Beatmap.Value.Track); + InputManager.Child = container = new TestPlayerLoaderContainer( loader = new TestPlayerLoader(() => { @@ -63,6 +68,24 @@ namespace osu.Game.Tests.Visual.Gameplay })); } + /// + /// When exits early, it has to wait for the player load task + /// to complete before running disposal on player. This previously caused an issue where mod + /// speed adjustments were undone too late, causing cross-screen pollution. + /// + [Test] + public void TestEarlyExit() + { + AddStep("load dummy beatmap", () => ResetPlayer(false, () => Mods.Value = new[] { new OsuModNightcore() })); + AddUntilStep("wait for current", () => loader.IsCurrentScreen()); + AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1); + AddStep("exit loader", () => loader.Exit()); + AddUntilStep("wait for not current", () => !loader.IsCurrentScreen()); + AddAssert("player did not load", () => !player.IsLoaded); + AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true); + AddAssert("mod rate still applied", () => Beatmap.Value.Track.Rate != 1); + } + [Test] public void TestBlockLoadViaMouseMovement() { @@ -196,6 +219,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public new VisualSettings VisualSettings => base.VisualSettings; + public new Task DisposalTask => base.DisposalTask; + public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 8014631eca..7bd40af512 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps.Drawables difficultyName.Text = beatmap.Version; starRating.Text = $"{beatmap.StarDifficulty:0.##}"; - difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating); + difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating, true); return true; } diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index af66f57f14..2dc12b3e67 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -38,7 +38,7 @@ namespace osu.Game.Graphics } } - public Color4 ForDifficultyRating(DifficultyRating difficulty) + public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterColour = false) { switch (difficulty) { @@ -56,10 +56,10 @@ namespace osu.Game.Graphics return Pink; case DifficultyRating.Expert: - return Purple; + return useLighterColour ? PurpleLight : Purple; case DifficultyRating.ExpertPlus: - return Gray0; + return useLighterColour ? Gray9 : Gray0; } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 58f5f02956..f6723839b2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores return; for (int i = 0; i < value.Count; i++) - backgroundFlow.Add(new ScoreTableRowBackground(i)); + backgroundFlow.Add(new ScoreTableRowBackground(i, value[i])); Columns = createHeaders(value[0]); Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular(); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs index d820f4d89d..724a7f8b55 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Online.API; +using osu.Game.Scoring; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -17,8 +19,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly Box hoveredBackground; private readonly Box background; - public ScoreTableRowBackground(int index) + private readonly int index; + private readonly ScoreInfo score; + + public ScoreTableRowBackground(int index, ScoreInfo score) { + this.index = index; + this.score = score; + RelativeSizeAxes = Axes.X; Height = 25; @@ -37,16 +45,21 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Alpha = 0, }, }; - - if (index % 2 != 0) - background.Alpha = 0; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, IAPIProvider api) { - hoveredBackground.Colour = colours.Gray4; - background.Colour = colours.Gray3; + var isOwnScore = api.LocalUser.Value.Id == score.UserID; + + if (isOwnScore) + background.Colour = colours.GreenDarker; + else if (index % 2 == 0) + background.Colour = colours.Gray3; + else + background.Alpha = 0; + + hoveredBackground.Colour = isOwnScore ? colours.GreenDark : colours.Gray4; } protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index c6b4787ff1..69a4a4181a 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -167,10 +167,6 @@ namespace osu.Game.Overlays.Mods { switch (e.Button) { - case MouseButton.Left: - SelectNext(1); - break; - case MouseButton.Right: SelectNext(-1); break; @@ -180,6 +176,13 @@ namespace osu.Game.Overlays.Mods return true; } + protected override bool OnClick(ClickEvent e) + { + SelectNext(1); + + return true; + } + /// /// Select the next available mod in a specified direction. /// diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 5000156e97..1fa233d9d4 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -3,16 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Input.Bindings; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; -using osu.Game.Screens.Ranking; using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays { @@ -36,21 +34,21 @@ namespace osu.Game.Overlays protected override bool DimMainContent => false; // dimming is handled by main overlay - private class BackButton : OsuClickableContainer, IKeyBindingHandler + private class BackButton : OsuButton { - private AspectContainer aspect; - [BackgroundDependencyLoader] private void load() { Size = new Vector2(Sidebar.DEFAULT_WIDTH); - Children = new Drawable[] + + BackgroundColour = Color4.Black; + + AddRange(new Drawable[] { - aspect = new AspectContainer + new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, Children = new Drawable[] { new SpriteIcon @@ -71,34 +69,8 @@ namespace osu.Game.Overlays }, } } - }; + }); } - - protected override bool OnMouseDown(MouseDownEvent e) - { - aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(e); - } - - protected override bool OnMouseUp(MouseUpEvent e) - { - aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); - } - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.Back: - Click(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; } } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index ff78d85bf0..58c9a6a784 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -214,10 +214,13 @@ namespace osu.Game.Screens.Play base.Update(); } + private bool speedAdjustmentsApplied; + private void updateRate() { if (sourceClock == null) return; + speedAdjustmentsApplied = true; sourceClock.ResetSpeedAdjustments(); if (sourceClock is IHasTempoAdjust tempo) @@ -239,7 +242,12 @@ namespace osu.Game.Screens.Play private void removeSourceClockAdjustments() { - sourceClock.ResetSpeedAdjustments(); + if (speedAdjustmentsApplied) + { + sourceClock.ResetSpeedAdjustments(); + speedAdjustmentsApplied = false; + } + (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 87d902b547..57021dfc68 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -55,7 +55,9 @@ namespace osu.Game.Screens.Play protected override bool PlayResumeSound => false; - private Task loadTask; + protected Task LoadTask { get; private set; } + + protected Task DisposalTask { get; private set; } private InputManager inputManager; private IdleTracker idleTracker; @@ -159,7 +161,7 @@ namespace osu.Game.Screens.Play player.RestartCount = restartCount; player.RestartRequested = restartRequested; - loadTask = LoadComponentAsync(player, _ => info.Loading = false); + LoadTask = LoadComponentAsync(player, _ => info.Loading = false); } private void contentIn() @@ -250,7 +252,7 @@ namespace osu.Game.Screens.Play { if (!this.IsCurrentScreen()) return; - loadTask = null; + LoadTask = null; //By default, we want to load the player and never be returned to. //Note that this may change if the player we load requested a re-run. @@ -301,7 +303,7 @@ namespace osu.Game.Screens.Play if (isDisposing) { // if the player never got pushed, we should explicitly dispose it. - loadTask?.ContinueWith(_ => player.Dispose()); + DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose()); } } diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 9ca5d60cb0..fda031e6cb 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -29,13 +29,13 @@ namespace osu.Game.Skinning /// A function to create the default skin implementation of this element. /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// How (if at all) the should be resize to fit within our own bounds. - public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : this(component, allowFallback, confineMode) { createDefault = defaultImplementation; } - protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(allowFallback) { this.component = component; diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index e225bfc490..5352928ec6 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -19,7 +19,7 @@ namespace osu.Game.Skinning [Resolved] private TextureStore textures { get; set; } - public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(new SpriteComponent(textureName), allowFallback, confineMode) { } diff --git a/osu.Game/Skinning/SkinnableSpriteText.cs b/osu.Game/Skinning/SkinnableSpriteText.cs index e72f9c9811..567dd348e1 100644 --- a/osu.Game/Skinning/SkinnableSpriteText.cs +++ b/osu.Game/Skinning/SkinnableSpriteText.cs @@ -8,7 +8,7 @@ namespace osu.Game.Skinning { public class SkinnableSpriteText : SkinnableDrawable, IHasText { - public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(component, defaultImplementation, allowFallback, confineMode) { }