diff --git a/.gitignore b/.gitignore index e6b5db5904..732b171f69 100644 --- a/.gitignore +++ b/.gitignore @@ -331,3 +331,6 @@ fastlane/report.xml # inspectcode inspectcodereport.xml inspectcode + +# BenchmarkDotNet +/BenchmarkDotNet.Artifacts diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml new file mode 100644 index 0000000000..1815c271b4 --- /dev/null +++ b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml new file mode 100644 index 0000000000..d85a0ae44c --- /dev/null +++ b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index e7b691909e..6480612b2e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,7 @@ { "version": "0.2.0", - "configurations": [{ + "configurations": [ + { "name": "osu! (Debug)", "type": "coreclr", "request": "launch", @@ -50,7 +51,8 @@ } }, "console": "internalConsole" - }, { + }, + { "name": "osu! (Tests, Release)", "type": "coreclr", "request": "launch", @@ -139,6 +141,19 @@ }, "console": "internalConsole" }, + { + "name": "Benchmark", + "type": "coreclr", + "request": "launch", + "program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll", + "args": [ + "--filter", + "*" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "Build benchmarks", + "console": "internalConsole" + }, { "name": "Cake: Debug Script", "type": "coreclr", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c087800d2a..e638dec767 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,7 +2,8 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", - "tasks": [{ + "tasks": [ + { "label": "Build osu! (Debug)", "type": "shell", "command": "dotnet", @@ -78,7 +79,8 @@ ], "group": "build", "problemMatcher": "$msCompile" - }, { + }, + { "label": "Build tournament tests (Release)", "type": "shell", "command": "dotnet", @@ -94,6 +96,22 @@ "group": "build", "problemMatcher": "$msCompile" }, + { + "label": "Build benchmarks", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "--no-restore", + "osu.Game.Benchmarks", + "/p:Configuration=Release", + "/p:GenerateFullPaths=true", + "/m", + "/verbosity:m" + ], + "group": "build", + "problemMatcher": "$msCompile" + }, { "label": "Restore (netcoreapp3.1)", "type": "shell", diff --git a/Directory.Build.props b/Directory.Build.props index 27a0bd0d48..21b8b402e0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,7 +40,7 @@ https://github.com/ppy/osu Automated release. ppy Pty Ltd - Copyright (c) 2019 ppy Pty Ltd + Copyright (c) 2020 ppy Pty Ltd osu game \ No newline at end of file diff --git a/LICENCE b/LICENCE index 21c6a7090f..2435c23545 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -Copyright (c) 2019 ppy Pty Ltd . +Copyright (c) 2020 ppy Pty Ltd . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/osu.Android.props b/osu.Android.props index e1e6f2e478..9c0da1e9fd 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Desktop.slnf b/osu.Desktop.slnf index e6b6446f72..d2c14d321a 100644 --- a/osu.Desktop.slnf +++ b/osu.Desktop.slnf @@ -3,6 +3,7 @@ "path": "osu.sln", "projects": [ "osu.Desktop\\osu.Desktop.csproj", + "osu.Game.Benchmarks\\osu.Game.Benchmarks.csproj", "osu.Game.Rulesets.Catch.Tests\\osu.Game.Rulesets.Catch.Tests.csproj", "osu.Game.Rulesets.Catch\\osu.Game.Rulesets.Catch.csproj", "osu.Game.Rulesets.Mania.Tests\\osu.Game.Rulesets.Mania.Tests.csproj", diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 80bb82c769..08cc0e7f5f 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -75,6 +75,9 @@ namespace osu.Desktop private void updateStatus() { + if (!client.IsInitialized) + return; + if (status.Value is UserStatusOffline) { client.ClearPresence(); diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 141b2cdbbc..bd91bcc933 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -22,8 +22,9 @@ namespace osu.Desktop { // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; + bool useSdl = args.Contains("--sdl"); - using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) + using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useSdl: useSdl)) { host.ExceptionThrown += handleException; diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec index a26b35fcd5..a919d54f38 100644 --- a/osu.Desktop/osu.nuspec +++ b/osu.Desktop/osu.nuspec @@ -12,7 +12,7 @@ click the circles. to the beat. click the circles. testing - Copyright (c) 2019 ppy Pty Ltd + Copyright (c) 2020 ppy Pty Ltd en-AU diff --git a/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs b/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs new file mode 100644 index 0000000000..394fd75488 --- /dev/null +++ b/osu.Game.Benchmarks/BenchmarkBeatmapParsing.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using BenchmarkDotNet.Attributes; +using osu.Framework.IO.Stores; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO; +using osu.Game.IO.Archives; +using osu.Game.Resources; + +namespace osu.Game.Benchmarks +{ + public class BenchmarkBeatmapParsing : BenchmarkTest + { + private readonly MemoryStream beatmapStream = new MemoryStream(); + + public override void SetUp() + { + using (var resources = new DllResourceStore(OsuResources.ResourceAssembly)) + using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz")) + using (var reader = new ZipArchiveReader(archive)) + reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream); + } + + [Benchmark] + public Beatmap BenchmarkBundledBeatmap() + { + beatmapStream.Seek(0, SeekOrigin.Begin); + var reader = new LineBufferedReader(beatmapStream); // no disposal + + var decoder = Decoder.GetDecoder(reader); + return decoder.Decode(reader); + } + } +} diff --git a/osu.Game.Benchmarks/BenchmarkTest.cs b/osu.Game.Benchmarks/BenchmarkTest.cs new file mode 100644 index 0000000000..34f5edd084 --- /dev/null +++ b/osu.Game.Benchmarks/BenchmarkTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using NUnit.Framework; + +namespace osu.Game.Benchmarks +{ + [TestFixture] + [MemoryDiagnoser] + public abstract class BenchmarkTest + { + [GlobalSetup] + [OneTimeSetUp] + public virtual void SetUp() + { + } + + [Test] + public void RunBenchmark() => BenchmarkRunner.Run(GetType()); + } +} diff --git a/osu.Game.Benchmarks/Program.cs b/osu.Game.Benchmarks/Program.cs new file mode 100644 index 0000000000..c55075fea6 --- /dev/null +++ b/osu.Game.Benchmarks/Program.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using BenchmarkDotNet.Running; + +namespace osu.Game.Benchmarks +{ + public static class Program + { + public static void Main(string[] args) + { + BenchmarkSwitcher + .FromAssembly(typeof(Program).Assembly) + .Run(args); + } + } +} diff --git a/osu.Game.Benchmarks/Properties/launchSettings.json b/osu.Game.Benchmarks/Properties/launchSettings.json new file mode 100644 index 0000000000..c1868088f9 --- /dev/null +++ b/osu.Game.Benchmarks/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "All Benchmarks": { + "commandName": "Project", + "commandLineArgs": "--filter *" + } + } +} \ No newline at end of file diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj new file mode 100644 index 0000000000..f2e1c0ec3b --- /dev/null +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + Exe + false + + + + + + + + + + + + + diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 493ac7ae39..f4749be370 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; using NUnit.Framework; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; 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 0b2862e5bb..9559d13328 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 @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 958cd19d50..53a018c9f4 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 6c8515eb90..4649dcae90 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -3,7 +3,7 @@ using System; using System.Linq; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Replays; using osu.Game.Rulesets.Catch.Beatmaps; diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 22532bc9ec..f122588a2b 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Input.StateChanges; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Replays; using osu.Game.Rulesets.Replays; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 2d6ce02e45..0c8c483048 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -7,7 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 12865385b6..d0ff1fab43 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; 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 9d362e5819..dea6e6c0fb 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 @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 1a77a4944b..d904474815 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -5,7 +5,7 @@ using osu.Game.Rulesets.Mania.Objects; using System; using System.Linq; using System.Collections.Generic; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 9565ac8994..315ef96e49 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.MathUtils; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs index 39185e6a57..4c125ad6ef 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods { public override string Name => "Fade In"; public override string Acronym => "FI"; - public override IconUsage Icon => OsuIcon.ModHidden; + public override IconUsage? Icon => OsuIcon.ModHidden; public override ModType Type => ModType.DifficultyIncrease; public override string Description => @"Keys appear out of nowhere!"; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs index 9275371a61..14b36fb765 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs @@ -4,7 +4,7 @@ using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Beatmaps; @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Random"; public override string Acronym => "RD"; public override ModType Type => ModType.Conversion; - public override IconUsage Icon => OsuIcon.Dice; + public override IconUsage? Icon => OsuIcon.Dice; public override string Description => @"Shuffle around the keys!"; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs index ee2cec1bbd..90e78c3899 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs @@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components { public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour { - private const float hit_target_bar_height = 2; - private readonly IBindable direction = new Bindable(); private readonly Drawable hitTarget; @@ -67,6 +65,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components private class DefaultHitTarget : CompositeDrawable, IHasAccentColour { + private const float hit_target_bar_height = 2; + private readonly IBindable direction = new Bindable(); private readonly Container hitTargetLine; diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index ccbff226a9..35de47e208 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -5,7 +5,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osuTK; using osuTK.Graphics; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 450f7de6d2..cd3daf18a9 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Beatmaps; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs index c9b3d08a22..4af4d5f966 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Beatmaps; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs index 863d0eda09..d692be89b2 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs index 013920684c..5dd2bd18a8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index bd9d948782..5cf571d961 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 65d7acc911..fe46876050 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Autopilot"; public override string Acronym => "AP"; - public override IconUsage Icon => OsuIcon.ModAutopilot; + public override IconUsage? Icon => OsuIcon.ModAutopilot; public override ModType Type => ModType.Automation; public override string Description => @"Automatic cursor movement - just follow the rhythm."; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 831e4a700f..937473e824 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "Play with blinds on your screen."; public override string Acronym => "BL"; - public override IconUsage Icon => FontAwesome.Solid.Adjust; + public override IconUsage? Icon => FontAwesome.Solid.Adjust; public override ModType Type => ModType.DifficultyIncrease; public override bool Ranked => false; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index 9bf7525d33..73cb483ef0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Acronym => "DF"; - public override IconUsage Icon => FontAwesome.Solid.CompressArrowsAlt; + public override IconUsage? Icon => FontAwesome.Solid.CompressArrowsAlt; public override string Description => "Hit them at the right size!"; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 778c2f7d43..ac20407ed2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -8,7 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 76676ce888..f08d4e8f5e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Acronym => "GR"; - public override IconUsage Icon => FontAwesome.Solid.ArrowsAltV; + public override IconUsage? Icon => FontAwesome.Solid.ArrowsAltV; public override string Description => "Hit them at the right size!"; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index eae218509e..940c888f3a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Spin In"; public override string Acronym => "SI"; - public override IconUsage Icon => FontAwesome.Solid.Undo; + public override IconUsage? Icon => FontAwesome.Solid.Undo; public override ModType Type => ModType.Fun; public override string Description => "Circles spin in. No approach circles."; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 1cdcddbd33..9d5d300a9e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Spun Out"; public override string Acronym => "SO"; - public override IconUsage Icon => OsuIcon.ModSpunout; + public override IconUsage? Icon => OsuIcon.ModSpunout; public override ModType Type => ModType.DifficultyReduction; public override string Description => @"Spinners will be automatically completed."; public override double ScoreMultiplier => 0.9; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 8360e2692e..2464308347 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Name => "Target"; public override string Acronym => "TP"; public override ModType Type => ModType.Conversion; - public override IconUsage Icon => OsuIcon.ModTarget; + public override IconUsage? Icon => OsuIcon.ModTarget; public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index dff9a77807..774f9cf58b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Bindables; using System.Collections.Generic; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; @@ -19,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Traceable"; public override string Acronym => "TC"; - public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost; public override ModType Type => ModType.Fun; public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index a9475af638..cc664ae72e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Transform"; public override string Acronym => "TR"; - public override IconUsage Icon => FontAwesome.Solid.ArrowsAlt; + public override IconUsage? Icon => FontAwesome.Solid.ArrowsAlt; public override ModType Type => ModType.Fun; public override string Description => "Everything rotates. EVERYTHING."; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index 1664a37a66..cc2f4c3f70 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Wiggle"; public override string Acronym => "WG"; - public override IconUsage Icon => FontAwesome.Solid.Certificate; + public override IconUsage? Icon => FontAwesome.Solid.Certificate; public override ModType Type => ModType.Fun; public override string Description => "They just won't stay still..."; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index b81d94a673..20b31c68f2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -6,13 +6,11 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Scoring; using osuTK; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -23,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private double animDuration; - private readonly SkinnableDrawable scaleContainer; + private readonly Drawable scaleContainer; public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint) @@ -36,16 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Blending = BlendingParameters.Additive; Origin = Anchor.Centre; - InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon - { - RelativeSizeAxes = Axes.Both, - Icon = FontAwesome.Solid.ChevronRight, - Size = new Vector2(0.35f) - }, confineMode: ConfineMode.NoScaling) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; + InternalChild = scaleContainer = new ReverseArrowPiece(); } private readonly IBindable scaleBindable = new Bindable(); @@ -65,11 +54,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateInitialTransforms() { - animDuration = Math.Min(150, repeatPoint.SpanDuration / 2); + animDuration = Math.Min(300, repeatPoint.SpanDuration); this.Animate( d => d.FadeIn(animDuration), - d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf) + d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf) ); } @@ -88,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; case ArmedState.Hit: - this.FadeOut(animDuration, Easing.OutQuint) + this.FadeOut(animDuration, Easing.Out) .ScaleTo(Scale * 1.5f, animDuration, Easing.Out); break; } @@ -121,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; } - float aimRotation = MathHelper.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X)); + float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X)); while (Math.Abs(aimRotation - Rotation) > 180) aimRotation += aimRotation < Rotation ? 360 : -360; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs new file mode 100644 index 0000000000..35a27bb0a6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs @@ -0,0 +1,43 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; +using osuTK; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class ReverseArrowPiece : BeatSyncedContainer + { + public ReverseArrowPiece() + { + Divisor = 2; + MinimumBeatLength = 200; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Blending = BlendingParameters.Additive; + + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + + Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(0.35f) + }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) => + Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index 8a8668d6af..e24fa865ad 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void setRange(double p0, double p1) { if (p0 > p1) - MathHelper.Swap(ref p0, ref p1); + (p0, p1) = (p1, p0); if (SnakedStart == p0 && SnakedEnd == p1) return; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index c45e98cc76..e3dd2b1b4f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -8,6 +8,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osuTK; using osuTK.Graphics; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { base.Update(); - var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); + var thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs index 97a7b98c5b..80ab03c45c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 2686ba4fd2..4cb2cd6539 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osuTK; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using System; diff --git a/osu.Game.Rulesets.Osu/Replays/OsuFramedReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuFramedReplayInputHandler.cs index c6ac1dd346..b42e9ac187 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuFramedReplayInputHandler.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Input.StateChanges; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Replays; using osu.Game.Rulesets.Replays; using osuTK; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs index d41135ca69..21df49d80b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs @@ -3,7 +3,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osuTK.Graphics; diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 28f5d4d301..f23fd6d3f9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Tests.Beatmaps; diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index b2c8c7feda..c01eef5252 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -7,7 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; 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 da89b37fbf..d728d65bfd 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 @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 338fd9e20f..5806c90115 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4766411cbd..c1bd73ef05 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -13,7 +14,9 @@ using osu.Game.IPC; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; using osu.Game.IO; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Resources; using SharpCompress.Archives; using SharpCompress.Archives.Zip; @@ -552,6 +555,83 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestUpdateBeatmapInfo() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfo))) + { + try + { + var osu = loadOsu(host); + var manager = osu.Dependencies.Get(); + + var temp = TestResources.GetTestBeatmapForImport(); + await osu.Dependencies.Get().Import(temp); + + // Update via the beatmap, not the beatmap info, to ensure correct linking + BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; + beatmapToUpdate.BeatmapInfo.Version = "updated"; + + manager.Update(setToUpdate); + + BeatmapInfo updatedInfo = manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID); + Assert.That(updatedInfo.Version, Is.EqualTo("updated")); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestUpdateBeatmapFile() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFile))) + { + try + { + var osu = loadOsu(host); + var manager = osu.Dependencies.Get(); + + var temp = TestResources.GetTestBeatmapForImport(); + await osu.Dependencies.Get().Import(temp); + + BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; + BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); + + using (var stream = new MemoryStream()) + { + using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + beatmapToUpdate.HitObjects.Clear(); + beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); + + new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); + } + + stream.Seek(0, SeekOrigin.Begin); + + manager.UpdateFile(setToUpdate, fileToUpdate, stream); + } + + // Check that the old file reference has been removed + Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID)); + + // Check that the new file is referenced correctly by attempting a retrieval + Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; + Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); + Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); + } + finally + { + host.Exit(); + } + } + } + public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index eec52669ff..244e37f017 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; diff --git a/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs index da8702209c..fd248abbc9 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneZoomableScrollContainer.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index e3688c276f..72fc6d8bd2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -6,7 +6,7 @@ using osu.Game.Rulesets.Objects; using System; using System.Collections.Generic; using osu.Game.Rulesets.Judgements; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 36235a4418..46f62b9541 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 5336c720a1..78c3b22fb9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -7,7 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs index ad747e88e1..e7b3e007fc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Screens.Play; using osuTK.Input; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index 0150c6ea74..563d6be0da 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu; diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index f68f4b8b83..ad5950d9fc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -12,7 +12,7 @@ using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Screens; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -146,6 +146,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("player mods applied", () => playerMod2.Applied); } + [Test] + public void TestModDisplayChanges() + { + var testMod = new TestMod(); + + AddStep("load player", () => ResetPlayer(true)); + + AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); + AddStep("set test mod in loader", () => loader.Mods.Value = new[] { testMod }); + AddAssert("test mod is displayed", () => (TestMod)loader.DisplayedMods.Single() == testMod); + } + [Test] public void TestMutedNotificationMasterVolume() => addVolumeSteps("master volume", () => audioManager.Volume.Value = 0, null, () => audioManager.Volume.IsDefault); @@ -221,6 +233,8 @@ namespace osu.Game.Tests.Visual.Gameplay public new Task DisposalTask => base.DisposalTask; + public IReadOnlyList DisplayedMods => MetadataInfo.Mods.Value; + public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs index 080a287b48..ffd6f55b53 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoreCounter.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index af21007efe..9a217ae416 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Play; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs index 68ad0b42b4..1e3e06ce7a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs @@ -7,7 +7,7 @@ using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Match.Components; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Audio; using osu.Framework.Allocation; diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index 96c0c59695..990e0a166b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Overlays.BeatmapSet; using osu.Game.Screens.Select.Details; diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index 80fad44593..2b572c1f6c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -8,7 +8,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Overlays.BeatmapSet; using osu.Game.Screens.Select.Details; diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs index 16e47c5df9..1fb3f4ba45 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs @@ -5,11 +5,12 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osu.Game.Overlays.Chat.Tabs; @@ -25,7 +26,7 @@ namespace osu.Game.Tests.Visual.Online typeof(ChannelTabControl), }; - private readonly ChannelTabControl channelTabControl; + private readonly TestTabControl channelTabControl; public TestSceneChannelTabControl() { @@ -37,7 +38,7 @@ namespace osu.Game.Tests.Visual.Online Anchor = Anchor.Centre, Children = new Drawable[] { - channelTabControl = new ChannelTabControl + channelTabControl = new TestTabControl { RelativeSizeAxes = Axes.X, Origin = Anchor.Centre, @@ -73,32 +74,40 @@ namespace osu.Game.Tests.Visual.Online channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue; AddStep("Add random private channel", addRandomPrivateChannel); - AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2); + AddAssert("There is only one channels", () => channelTabControl.Items.Count == 2); AddRepeatStep("Add 3 random private channels", addRandomPrivateChannel, 3); - AddAssert("There are four channels", () => channelTabControl.Items.Count() == 5); + AddAssert("There are four channels", () => channelTabControl.Items.Count == 5); AddStep("Add random public channel", () => addChannel(RNG.Next().ToString())); - AddRepeatStep("Select a random channel", () => channelTabControl.Current.Value = channelTabControl.Items.ElementAt(RNG.Next(channelTabControl.Items.Count() - 1)), 20); + AddRepeatStep("Select a random channel", () => + { + List validChannels = channelTabControl.Items.Where(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)).ToList(); + channelTabControl.SelectChannel(validChannels[RNG.Next(0, validChannels.Count)]); + }, 20); - Channel channelBefore = channelTabControl.Items.First(); - AddStep("set first channel", () => channelTabControl.Current.Value = channelBefore); + Channel channelBefore = null; + AddStep("set first channel", () => channelTabControl.SelectChannel(channelBefore = channelTabControl.Items.First(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)))); - AddStep("select selector tab", () => channelTabControl.Current.Value = channelTabControl.Items.Last()); + AddStep("select selector tab", () => channelTabControl.SelectChannel(channelTabControl.Items.Single(c => c is ChannelSelectorTabItem.ChannelSelectorTabChannel))); AddAssert("selector tab is active", () => channelTabControl.ChannelSelectorActive.Value); AddAssert("check channel unchanged", () => channelBefore == channelTabControl.Current.Value); - AddStep("set second channel", () => channelTabControl.Current.Value = channelTabControl.Items.Skip(1).First()); + AddStep("set second channel", () => channelTabControl.SelectChannel(channelTabControl.Items.GetNext(channelBefore))); AddAssert("selector tab is inactive", () => !channelTabControl.ChannelSelectorActive.Value); AddUntilStep("remove all channels", () => { - var first = channelTabControl.Items.First(); - if (first is ChannelSelectorTabItem.ChannelSelectorTabChannel) - return true; + foreach (var item in channelTabControl.Items.ToList()) + { + if (item is ChannelSelectorTabItem.ChannelSelectorTabChannel) + continue; - channelTabControl.RemoveChannel(first); - return false; + channelTabControl.RemoveChannel(item); + return false; + } + + return true; }); AddAssert("selector tab is active", () => channelTabControl.ChannelSelectorActive.Value); @@ -117,5 +126,10 @@ namespace osu.Game.Tests.Visual.Online Type = ChannelType.Public, Name = name }); + + private class TestTabControl : ChannelTabControl + { + public void SelectChannel(Channel channel) => base.SelectTab(TabMap[channel]); + } } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs new file mode 100644 index 0000000000..7ac65181f9 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs @@ -0,0 +1,76 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsCountryFilter : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(CountryFilter), + typeof(CountryPill) + }; + + public TestSceneRankingsCountryFilter() + { + var countryBindable = new Bindable(); + + AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new CountryFilter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Current = { BindTarget = countryBindable } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Some content", + Margin = new MarginPadding { Vertical = 20 } + } + } + } + }); + + var country = new Country + { + FlagName = "BY", + FullName = "Belarus" + }; + var unknownCountry = new Country + { + FlagName = "CK", + FullName = "Cook Islands" + }; + + AddStep("Set country", () => countryBindable.Value = country); + AddStep("Set null country", () => countryBindable.Value = null); + AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry); + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index b19f2dbf31..1b136d9e41 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets.Osu.Mods; diff --git a/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs b/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs index 4702d24125..f14c75084f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Overlays.Comments; -using osu.Framework.MathUtils; +using osu.Framework.Utils; namespace osu.Game.Tests.Visual.Online { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 57e297bcd5..3eff75b020 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -3,10 +3,13 @@ using System; using System.Collections.Generic; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; +using osu.Game.Overlays; +using osu.Game.Online.Placeholders; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; @@ -28,8 +31,16 @@ namespace osu.Game.Tests.Visual.SongSelect private readonly FailableLeaderboard leaderboard; + [Cached] + private readonly DialogOverlay dialogOverlay; + public TestSceneBeatmapLeaderboard() { + Add(dialogOverlay = new DialogOverlay + { + Depth = -1 + }); + Add(leaderboard = new FailableLeaderboard { Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 00fa95bedc..eb812f5d5a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -11,7 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs index e34e1844ce..0598324110 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; using osu.Game.Scoring; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select.Leaderboards; @@ -15,10 +17,18 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneUserTopScoreContainer : OsuTestScene { + [Cached] + private readonly DialogOverlay dialogOverlay; + public TestSceneUserTopScoreContainer() { UserTopScoreContainer topScoreContainer; + Add(dialogOverlay = new DialogOverlay + { + Depth = -1 + }); + Add(new Container { Origin = Anchor.BottomCentre, diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index e95f4c09c6..d1dde4664a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; using osuTK; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs new file mode 100644 index 0000000000..1e5e26e4c5 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -0,0 +1,181 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Leaderboards; +using osu.Game.Online.Placeholders; +using osu.Game.Overlays; +using osu.Game.Rulesets; +using osu.Game.Scoring; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Tests.Resources; +using osu.Game.Users; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneDeleteLocalScore : ManualInputManagerTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(Placeholder), + typeof(MessagePlaceholder), + typeof(RetrievalFailurePlaceholder), + typeof(UserTopScoreContainer), + typeof(Leaderboard), + typeof(LeaderboardScore), + }; + + private readonly ContextMenuContainer contextMenuContainer; + private readonly BeatmapLeaderboard leaderboard; + + private RulesetStore rulesetStore; + private BeatmapManager beatmapManager; + private ScoreManager scoreManager; + + private readonly List scores = new List(); + private BeatmapInfo beatmap; + + [Cached] + private readonly DialogOverlay dialogOverlay; + + public TestSceneDeleteLocalScore() + { + Children = new Drawable[] + { + contextMenuContainer = new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = leaderboard = new BeatmapLeaderboard + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(550f, 450f), + Scope = BeatmapLeaderboardScope.Local, + Beatmap = new BeatmapInfo + { + ID = 1, + Metadata = new BeatmapMetadata + { + ID = 1, + Title = "TestSong", + Artist = "TestArtist", + Author = new User + { + Username = "TestAuthor" + }, + }, + Version = "Insane" + }, + } + }, + dialogOverlay = new DialogOverlay() + }; + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); + dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, Audio, dependencies.Get(), Beatmap.Default)); + dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory)); + + beatmap = beatmapManager.Import(TestResources.GetTestBeatmapForImport()).Result.Beatmaps[0]; + + for (int i = 0; i < 50; i++) + { + var score = new ScoreInfo + { + OnlineScoreID = i, + Beatmap = beatmap, + BeatmapInfoID = beatmap.ID, + Accuracy = RNG.NextDouble(), + TotalScore = RNG.Next(1, 1000000), + MaxCombo = RNG.Next(1, 1000), + Rank = ScoreRank.XH, + User = new User { Username = "TestUser" }, + }; + + scores.Add(scoreManager.Import(score).Result); + } + + scores.Sort(Comparer.Create((s1, s2) => s2.TotalScore.CompareTo(s1.TotalScore))); + + return dependencies; + } + + [SetUp] + public void Setup() => Schedule(() => + { + // Due to soft deletions, we can re-use deleted scores between test runs + scoreManager.Undelete(scoreManager.QueryScores(s => s.DeletePending).ToList()); + + leaderboard.Scores = null; + leaderboard.FinishTransforms(true); // After setting scores, we may be waiting for transforms to expire drawables + + leaderboard.Beatmap = beatmap; + leaderboard.RefreshScores(); // Required in the case that the beatmap hasn't changed + }); + + [SetUpSteps] + public void SetupSteps() + { + // Ensure the leaderboard has finished async-loading drawables + AddUntilStep("wait for drawables", () => leaderboard.ChildrenOfType().Any()); + + // Ensure the leaderboard items have finished showing up + AddStep("finish transforms", () => leaderboard.FinishTransforms(true)); + } + + [Test] + public void TestDeleteViaRightClick() + { + AddStep("open menu for top score", () => + { + InputManager.MoveMouseTo(leaderboard.ChildrenOfType().First()); + InputManager.Click(MouseButton.Right); + }); + + // Ensure the context menu has finished showing + AddStep("finish transforms", () => contextMenuContainer.FinishTransforms(true)); + + AddStep("click delete option", () => + { + InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType().First(i => i.Item.Text.Value.ToLowerInvariant() == "delete")); + InputManager.Click(MouseButton.Left); + }); + + // Ensure the dialog has finished showing + AddStep("finish transforms", () => dialogOverlay.FinishTransforms(true)); + + AddStep("click delete button", () => + { + InputManager.MoveMouseTo(dialogOverlay.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scores[0].OnlineScoreID)); + } + + [Test] + public void TestDeleteViaDatabase() + { + AddStep("delete top score", () => scoreManager.Delete(scores[0])); + AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scores[0].OnlineScoreID)); + } + } +} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs index 54876dbbda..4e394b5ed8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osu.Game.Screens.Menu; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs new file mode 100644 index 0000000000..443cf59003 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneModButton : OsuTestScene + { + public TestSceneModButton() + { + Children = new Drawable[] + { + new ModButton(new MultiMod(new TestMod1(), new TestMod2(), new TestMod3(), new TestMod4())) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + } + }; + } + + private class TestMod1 : TestMod + { + public override string Name => "Test mod 1"; + + public override string Acronym => "M1"; + } + + private class TestMod2 : TestMod + { + public override string Name => "Test mod 2"; + + public override string Acronym => "M2"; + + public override IconUsage? Icon => FontAwesome.Solid.Exclamation; + } + + private class TestMod3 : TestMod + { + public override string Name => "Test mod 3"; + + public override string Acronym => "M3"; + + public override IconUsage? Icon => FontAwesome.Solid.ArrowRight; + } + + private class TestMod4 : TestMod + { + public override string Name => "Test mod 4"; + + public override string Acronym => "M4"; + } + + private abstract class TestMod : Mod, IApplicableMod + { + public override double ScoreMultiplier => 1.0; + } + } +} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 35e5f9719c..f8ace73168 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -8,7 +8,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index e3daa9c279..2ea9aec50a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -4,8 +4,10 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Timing; +using osu.Framework.Utils; +using osu.Game.Beatmaps; using osu.Game.Overlays; +using osu.Game.Rulesets.Osu; namespace osu.Game.Tests.Visual.UserInterface { @@ -15,22 +17,48 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private MusicController musicController = new MusicController(); - public TestSceneNowPlayingOverlay() - { - Clock = new FramedClock(); + private WorkingBeatmap currentBeatmap; - var np = new NowPlayingOverlay + private NowPlayingOverlay nowPlayingOverlay; + + [BackgroundDependencyLoader] + private void load() + { + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + + nowPlayingOverlay = new NowPlayingOverlay { Origin = Anchor.Centre, Anchor = Anchor.Centre }; Add(musicController); - Add(np); + Add(nowPlayingOverlay); + } - AddStep(@"show", () => np.Show()); + [Test] + public void TestShowHideDisable() + { + AddStep(@"show", () => nowPlayingOverlay.Show()); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); - AddStep(@"show", () => np.Hide()); + AddStep(@"hide", () => nowPlayingOverlay.Hide()); + } + + [Test] + public void TestPrevTrackBehavior() + { + AddStep(@"Play track", () => + { + musicController.NextTrack(); + currentBeatmap = Beatmap.Value; + }); + + AddStep(@"Seek track to 6 second", () => musicController.SeekTo(6000)); + AddUntilStep(@"Wait for current time to update", () => currentBeatmap.Track.CurrentTime > 5000); + AddAssert(@"Check action is restart track", () => musicController.PreviousTrack() == PreviousTrackResult.Restart); + AddUntilStep("Wait for current time to update", () => Precision.AlmostEquals(currentBeatmap.Track.CurrentTime, 0)); + AddAssert(@"Check track didn't change", () => currentBeatmap == Beatmap.Value); + AddAssert(@"Check action is not restart", () => musicController.PreviousTrack() != PreviousTrackResult.Restart); } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaders.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaders.cs index bede4e38b8..09326247f3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaders.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaders.cs @@ -54,10 +54,10 @@ namespace osu.Game.Tests.Visual.UserInterface } }); - addHeader("OverlayHeader", new TestNoControlHeader()); - addHeader("TabControlOverlayHeader (string)", new TestStringTabControlHeader()); - addHeader("TabControlOverlayHeader (enum)", new TestEnumTabControlHeader()); - addHeader("BreadcrumbControlOverlayHeader", new TestBreadcrumbControlHeader()); + addHeader("Blue OverlayHeader", new TestNoControlHeader(OverlayColourScheme.Blue)); + addHeader("Green TabControlOverlayHeader (string)", new TestStringTabControlHeader(OverlayColourScheme.Green)); + addHeader("Pink TabControlOverlayHeader (enum)", new TestEnumTabControlHeader(OverlayColourScheme.Pink)); + addHeader("Red BreadcrumbControlOverlayHeader", new TestBreadcrumbControlHeader(OverlayColourScheme.Red)); } private void addHeader(string name, OverlayHeader header) @@ -91,10 +91,9 @@ namespace osu.Game.Tests.Visual.UserInterface protected override ScreenTitle CreateTitle() => new TestTitle(); - [BackgroundDependencyLoader] - private void load(OsuColour colours) + public TestNoControlHeader(OverlayColourScheme colourScheme) + : base(colourScheme) { - TitleBackgroundColour = colours.GreyVioletDarker; } } @@ -104,34 +103,24 @@ namespace osu.Game.Tests.Visual.UserInterface protected override ScreenTitle CreateTitle() => new TestTitle(); - public TestStringTabControlHeader() + public TestStringTabControlHeader(OverlayColourScheme colourScheme) + : base(colourScheme) { TabControl.AddItem("tab1"); TabControl.AddItem("tab2"); } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - TitleBackgroundColour = colours.GreyVioletDarker; - ControlBackgroundColour = colours.GreyVioletDark; - TabControl.AccentColour = colours.Violet; - } } private class TestEnumTabControlHeader : TabControlOverlayHeader { + public TestEnumTabControlHeader(OverlayColourScheme colourScheme) + : base(colourScheme) + { + } + protected override Drawable CreateBackground() => new TestBackground(); protected override ScreenTitle CreateTitle() => new TestTitle(); - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - TitleBackgroundColour = colours.GreyVioletDarker; - ControlBackgroundColour = colours.GreyVioletDark; - TabControl.AccentColour = colours.Violet; - } } private enum TestEnum @@ -147,20 +136,13 @@ namespace osu.Game.Tests.Visual.UserInterface protected override ScreenTitle CreateTitle() => new TestTitle(); - public TestBreadcrumbControlHeader() + public TestBreadcrumbControlHeader(OverlayColourScheme colourScheme) + : base(colourScheme) { BreadcrumbControl.AddItem("tab1"); BreadcrumbControl.AddItem("tab2"); BreadcrumbControl.Current.Value = "tab2"; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - TitleBackgroundColour = colours.GreyVioletDarker; - ControlBackgroundColour = colours.GreyVioletDark; - BreadcrumbControl.AccentColour = colours.Violet; - } } private class TestBackground : Sprite @@ -186,12 +168,6 @@ namespace osu.Game.Tests.Visual.UserInterface Section = "section"; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Violet; - } - protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog"); } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs index 0cb8683d72..7386e0fa1f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -62,7 +61,7 @@ namespace osu.Game.Tests.Visual.UserInterface waitForCurrent(); pushNext(); waitForCurrent(); - AddAssert(@"only 2 items", () => breadcrumbs.Items.Count() == 2); + AddAssert(@"only 2 items", () => breadcrumbs.Items.Count == 2); AddStep(@"exit current", () => screenStack.CurrentScreen.Exit()); AddAssert(@"current screen is first", () => startScreen == screenStack.CurrentScreen); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs index 0da256855a..9738f73548 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets; namespace osu.Game.Tests.Visual.UserInterface @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Select random", () => { - selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count())); + selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count)); }); AddStep("Toggle disabled state", () => selector.Current.Disabled = !selector.Current.Disabled); } diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 3f8a548fd5..6c799e5e90 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs index 72d9eb0e07..77119f7a60 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs @@ -3,7 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Gameplay.Components; diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 75b88be1ab..7ecfd6ef70 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -7,7 +7,7 @@ - + WinExe diff --git a/osu.Game.Tournament/Screens/Drawings/Components/VisualiserContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/VisualiserContainer.cs index 1cd942b987..f21f5c9460 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/VisualiserContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/VisualiserContainer.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.MathUtils; +using osu.Framework.Utils; namespace osu.Game.Tournament.Screens.Drawings.Components { diff --git a/osu.Game.Tournament/TournamentFont.cs b/osu.Game.Tournament/TournamentFont.cs index f9e60ff2bc..32f0264562 100644 --- a/osu.Game.Tournament/TournamentFont.cs +++ b/osu.Game.Tournament/TournamentFont.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tournament string weightString = weight.ToString(); // Only exo has an explicit "regular" weight, other fonts do not - if (weight == FontWeight.Regular && family != GetFamilyString(TournamentTypeface.Aquatico) && family != GetFamilyString(TournamentTypeface.Aquatico)) + if (weight == FontWeight.Regular && family != GetFamilyString(TournamentTypeface.Aquatico)) weightString = string.Empty; return weightString; diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index bd91ad9704..17d7dc0245 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -223,9 +223,12 @@ namespace osu.Game.Tournament foreach (var r in ladder.Rounds) { - foreach (var b in r.Beatmaps) + foreach (var b in r.Beatmaps.ToList()) { - if (b.BeatmapInfo == null && b.ID > 0) + if (b.BeatmapInfo != null) + continue; + + if (b.ID > 0) { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); API.Perform(req); @@ -233,6 +236,10 @@ namespace osu.Game.Tournament addedInfo = true; } + + if (b.BeatmapInfo == null) + // if online population couldn't be performed, ensure we don't leave a null value behind + r.Beatmaps.Remove(b); } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a10ad73817..31869f9310 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -26,6 +27,8 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; +using Decoder = osu.Game.Beatmaps.Formats.Decoder; +using ZipArchive = SharpCompress.Archives.Zip.ZipArchive; namespace osu.Game.Beatmaps { @@ -56,14 +59,11 @@ namespace osu.Game.Beatmaps protected override string ImportFromStablePath => "Songs"; private readonly RulesetStore rulesets; - private readonly BeatmapStore beatmaps; - private readonly AudioManager audioManager; - private readonly GameHost host; - private readonly BeatmapUpdateQueue updateQueue; + private readonly Storage exportStorage; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null) @@ -80,6 +80,7 @@ namespace osu.Game.Beatmaps beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); updateQueue = new BeatmapUpdateQueue(api); + exportStorage = storage.GetStorageForDirectory("exports"); } protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => @@ -174,6 +175,50 @@ namespace osu.Game.Beatmaps /// The beatmap difficulty to restore. public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); + /// + /// Saves an file against a given . + /// + /// The to save the content against. The file referenced by will be replaced. + /// The content to write. + public void Save(BeatmapInfo info, IBeatmap beatmapContent) + { + var setInfo = QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == info.ID)); + + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(beatmapContent).Encode(sw); + + stream.Seek(0, SeekOrigin.Begin); + + UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); + } + + var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); + if (working != null) + workingCache.Remove(working); + } + + /// + /// Exports a to an .osz package. + /// + /// The to export. + public void Export(BeatmapSetInfo set) + { + var localSet = QueryBeatmapSet(s => s.ID == set.ID); + + using (var archive = ZipArchive.Create()) + { + foreach (var file in localSet.Files) + archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath)); + + using (var outputStream = exportStorage.GetStream($"{set}.osz", FileAccess.Write, FileMode.Create)) + archive.SaveTo(outputStream); + + exportStorage.OpenInNativeExplorer(); + } + } + private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 7bd40af512..a3128e36c4 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -48,11 +48,9 @@ namespace osu.Game.Beatmaps.Drawables InternalChild = iconContainer = new Container { Size = new Vector2(20f) }; } - public string TooltipText { get; set; } - public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); - public object TooltipContent { get; set; } + public object TooltipContent { get; } [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 433becd8cc..09f40ce7b6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); // Todo: Not all countdown types are supported by lazer yet writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? '1' : '0')}")); - writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}")); + writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePointAt(double.MinValue).SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}")); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index b1b27278fe..c46634e72f 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Game.IO; using osu.Game.Storyboards; using osu.Game.Beatmaps.Legacy; +using osu.Framework.Utils; namespace osu.Game.Beatmaps.Formats { @@ -190,7 +191,7 @@ namespace osu.Game.Beatmaps.Formats { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue)); + timelineGroup?.Rotation.Add(easing, startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue)); break; } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 6aba5257f5..05c344b199 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -7,13 +7,11 @@ using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using osu.Game.Storyboards; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Statistics; -using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; @@ -76,21 +74,6 @@ namespace osu.Game.Beatmaps return AudioManager.Tracks.GetVirtual(length); } - /// - /// Saves the . - /// - /// The absolute path of the output file. - public string Save() - { - string directory = Path.Combine(Path.GetTempPath(), @"osu!"); - Directory.CreateDirectory(directory); - - var path = Path.Combine(directory, Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json"); - using (var sw = new StreamWriter(path)) - sw.WriteLine(Beatmap.Serialize()); - return path; - } - /// /// Creates a to convert a for a specified . /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 45fe034a70..5e237d2ecb 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -34,7 +34,7 @@ namespace osu.Game.Database /// The associated file join type. public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete - where TFileModel : INamedFileInfo, new() + where TFileModel : class, INamedFileInfo, new() { private const int import_queue_request_concurrency = 1; @@ -222,9 +222,8 @@ namespace osu.Game.Database { model = CreateModel(archive); - if (model == null) return Task.FromResult(null); - - model.Hash = computeHash(archive); + if (model == null) + return Task.FromResult(null); } catch (TaskCanceledException) { @@ -262,18 +261,24 @@ namespace osu.Game.Database /// /// In the case of no matching files, a hash will be generated from the passed archive's . /// - private string computeHash(ArchiveReader reader) + private string computeHash(TModel item, ArchiveReader reader = null) { // for now, concatenate all .osu files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); - foreach (string file in reader.Filenames.Where(f => HashableFileTypes.Any(f.EndsWith))) + foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(f.Filename.EndsWith))) { - using (Stream s = reader.GetStream(file)) + using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath)) s.CopyTo(hashable); } - return hashable.Length > 0 ? hashable.ComputeSHA2Hash() : reader.Name.ComputeSHA2Hash(); + if (hashable.Length > 0) + return hashable.ComputeSHA2Hash(); + + if (reader != null) + return reader.Name.ComputeSHA2Hash(); + + return item.Hash; } /// @@ -303,6 +308,7 @@ namespace osu.Game.Database LogForModel(item, "Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); + item.Hash = computeHash(item, archive); await Populate(item, archive, cancellationToken); @@ -358,12 +364,42 @@ namespace osu.Game.Database return item; }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap(); + public void UpdateFile(TModel model, TFileModel file, Stream contents) + { + using (var usage = ContextFactory.GetForWrite()) + { + // Dereference the existing file info, since the file model will be removed. + Files.Dereference(file.FileInfo); + + // Remove the file model. + usage.Context.Set().Remove(file); + + // Add the new file info and containing file model. + model.Files.Remove(file); + model.Files.Add(new TFileModel + { + Filename = file.Filename, + FileInfo = Files.Add(contents) + }); + + Update(model); + } + } + /// /// Perform an update of the specified item. - /// TODO: Support file changes. + /// TODO: Support file additions/removals. /// /// The item to update. - public void Update(TModel item) => ModelStore.Update(item); + public void Update(TModel item) + { + using (ContextFactory.GetForWrite()) + { + item.Hash = computeHash(item); + + ModelStore.Update(item); + } + } /// /// Delete an item from the manager. diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 71bf195a8d..4bd9df5f36 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -21,7 +21,7 @@ namespace osu.Game.Database /// The associated file join type. public abstract class DownloadableArchiveModelManager : ArchiveModelManager, IModelDownloader where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable - where TFileModel : INamedFileInfo, new() + where TFileModel : class, INamedFileInfo, new() { public event Action> DownloadBegan; diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index af492bacc9..b9c7b26e3e 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osuTK; using osuTK.Graphics; using System; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index b9ef279f5c..be9aefa359 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -38,6 +38,11 @@ namespace osu.Game.Graphics.Containers /// public int Divisor { get; set; } = 1; + /// + /// An optional minimum beat length. Any beat length below this will be multiplied by two until valid. + /// + public double MinimumBeatLength { get; set; } + /// /// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing. /// @@ -89,6 +94,9 @@ namespace osu.Game.Graphics.Containers double beatLength = timingPoint.BeatLength / Divisor; + while (beatLength < MinimumBeatLength) + beatLength *= 2; + int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (effectPoint.OmitFirstBarLine ? 1 : 0); // The beats before the start of the first control point are off by 1, this should do the trick diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 9735f6373d..e3a9a5fe9d 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Containers } public void AddUserLink(User user, Action creationParameters = null) - => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "View Profile"); + => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); private void createLink(IEnumerable drawables, LinkDetails link, string tooltipText, Action action = null) { diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index 23015e8bf5..dadd7d5240 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -4,7 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Screens.Menu; using osuTK; diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index bf743b90ed..4cd3934cde 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -9,7 +9,7 @@ using osuTK; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Configuration; -using osu.Framework.MathUtils; +using osu.Framework.Utils; namespace osu.Game.Graphics.Containers { diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 5a83d8e4ce..170ea63059 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -14,6 +14,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osuTK.Input; +using osu.Framework.Utils; namespace osu.Game.Graphics.Cursor { @@ -55,7 +56,7 @@ namespace osu.Game.Graphics.Cursor if (dragRotationState == DragRotationState.Rotating && distance > 0) { Vector2 offset = e.MousePosition - positionMouseDown; - float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f; + float degrees = MathUtils.RadiansToDegrees(MathF.Atan2(-offset.X, offset.Y)) + 24.3f; // Always rotate in the direction of least distance float diff = (degrees - activeCursor.Rotation) % 360; diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 2dc12b3e67..6767104576 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -3,6 +3,7 @@ using System; using osu.Game.Beatmaps; +using osuTK; using osuTK.Graphics; namespace osu.Game.Graphics @@ -63,6 +64,46 @@ namespace osu.Game.Graphics } } + public Color4 ForOverlayElement(OverlayColourScheme colourScheme, float saturation, float lightness, float opacity = 1) => Color4.FromHsl(new Vector4(getBaseHue(colourScheme), saturation, lightness, opacity)); + + // See https://github.com/ppy/osu-web/blob/4218c288292d7c810b619075471eaea8bbb8f9d8/app/helpers.php#L1463 + private static float getBaseHue(OverlayColourScheme colourScheme) + { + float hue; + + switch (colourScheme) + { + default: + throw new ArgumentException($@"{colourScheme} colour scheme does not provide a hue value in {nameof(getBaseHue)}."); + + case OverlayColourScheme.Red: + hue = 0; + break; + + case OverlayColourScheme.Pink: + hue = 333; + break; + + case OverlayColourScheme.Orange: + hue = 46; + break; + + case OverlayColourScheme.Green: + hue = 115; + break; + + case OverlayColourScheme.Purple: + hue = 255; + break; + + case OverlayColourScheme.Blue: + hue = 200; + break; + } + + return hue / 360f; + } + // See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less public readonly Color4 PurpleLighter = FromHex(@"eeeeff"); public readonly Color4 PurpleLight = FromHex(@"aa88ff"); @@ -165,4 +206,14 @@ namespace osu.Game.Graphics public readonly Color4 ContextMenuGray = FromHex(@"223034"); } + + public enum OverlayColourScheme + { + Red, + Pink, + Orange, + Green, + Purple, + Blue + } } diff --git a/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs index f2f6dd429b..bdc3cd4c49 100644 --- a/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Extensions.Color4Extensions; using osuTK; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -41,5 +42,17 @@ namespace osu.Game.Graphics.UserInterface this.FadeOut(transition_duration, Easing.OutQuint); loading.Hide(); } + + protected override bool Handle(UIEvent e) + { + switch (e) + { + // blocking scroll can cause weird behaviour when this layer is used within a ScrollContainer. + case ScrollEvent _: + return false; + } + + return base.Handle(e); + } } } diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs index 8c00cae08a..7225dbc66f 100644 --- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs +++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs @@ -57,6 +57,6 @@ namespace osu.Game.Graphics.UserInterface return true; } - public string TooltipText => "View in browser"; + public string TooltipText => "view in browser"; } } diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 418ad038f7..e4a4b1c50e 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -104,7 +104,7 @@ namespace osu.Game.Graphics.UserInterface private class CapsWarning : SpriteIcon, IHasTooltip { - public string TooltipText => @"Caps lock is active"; + public string TooltipText => @"caps lock is active"; public CapsWarning() { diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index ed8904db7e..6a7998d5fb 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -15,7 +15,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index 3ee572602b..586cd2ce84 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -4,7 +4,7 @@ using osuTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using System; using System.Linq; using osu.Framework.Graphics.Sprites; diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index e37567c72c..7763577a14 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -24,6 +24,7 @@ namespace osu.Game.Input.Bindings public IEnumerable GlobalKeyBindings => new[] { + new KeyBinding(InputKey.F6, GlobalAction.ToggleNowPlaying), new KeyBinding(InputKey.F8, GlobalAction.ToggleChat), new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial), new KeyBinding(InputKey.F10, GlobalAction.ToggleGameplayMouseButtons), @@ -137,5 +138,8 @@ namespace osu.Game.Input.Bindings [Description("Play / pause")] MusicPlay, + + [Description("Toggle now playing overlay")] + ToggleNowPlaying, } } diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 0956c749a2..30c1018c1e 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -16,7 +16,7 @@ namespace osu.Game.Online.API { protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest(Uri); - public T Result => ((JsonWebRequest)WebRequest).ResponseObject; + public T Result => ((OsuJsonWebRequest)WebRequest).ResponseObject; protected APIRequest() { @@ -30,16 +30,6 @@ namespace osu.Game.Online.API /// This will be scheduled to the API's internal scheduler (run on update thread automatically). /// public new event APISuccessHandler Success; - - private class OsuJsonWebRequest : JsonWebRequest - { - public OsuJsonWebRequest(string uri) - : base(uri) - { - } - - protected override string UserAgent => "osu!"; - } } /// @@ -162,16 +152,6 @@ namespace osu.Game.Online.API [JsonProperty("error")] public string ErrorMessage { get; set; } } - - private class OsuWebRequest : WebRequest - { - public OsuWebRequest(string uri) - : base(uri) - { - } - - protected override string UserAgent => "osu!"; - } } public class APIException : InvalidOperationException diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index baf494ebf9..bdc47aab8d 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Net.Http; using osu.Framework.Bindables; -using osu.Framework.IO.Network; namespace osu.Game.Online.API { @@ -166,7 +165,7 @@ namespace osu.Game.Online.API } } - private class AccessTokenRequest : JsonWebRequest + private class AccessTokenRequest : OsuJsonWebRequest { protected string GrantType; diff --git a/osu.Game/Online/API/OsuJsonWebRequest.cs b/osu.Game/Online/API/OsuJsonWebRequest.cs new file mode 100644 index 0000000000..4a45a8b261 --- /dev/null +++ b/osu.Game/Online/API/OsuJsonWebRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API +{ + public class OsuJsonWebRequest : JsonWebRequest + { + public OsuJsonWebRequest(string uri) + : base(uri) + { + } + + public OsuJsonWebRequest() + { + } + + protected override string UserAgent => "osu!"; + } +} diff --git a/osu.Game/Online/API/OsuWebRequest.cs b/osu.Game/Online/API/OsuWebRequest.cs new file mode 100644 index 0000000000..1d27899473 --- /dev/null +++ b/osu.Game/Online/API/OsuWebRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API +{ + public class OsuWebRequest : WebRequest + { + public OsuWebRequest(string uri) + : base(uri) + { + } + + public OsuWebRequest() + { + } + + protected override string UserAgent => "osu!"; + } +} diff --git a/osu.Game/Online/API/RegistrationRequest.cs b/osu.Game/Online/API/RegistrationRequest.cs index 349cd4de0c..f650e5c93b 100644 --- a/osu.Game/Online/API/RegistrationRequest.cs +++ b/osu.Game/Online/API/RegistrationRequest.cs @@ -2,11 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using Newtonsoft.Json; -using osu.Framework.IO.Network; namespace osu.Game.Online.API { - public class RegistrationRequest : WebRequest + public class RegistrationRequest : OsuWebRequest { internal string Username; internal string Email; diff --git a/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs b/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs new file mode 100644 index 0000000000..95a5d0acbd --- /dev/null +++ b/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Net.Http; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class MarkChannelAsReadRequest : APIRequest + { + private readonly Channel channel; + private readonly Message message; + + public MarkChannelAsReadRequest(Channel channel, Message message) + { + this.channel = channel; + this.message = message; + } + + protected override string Target => $"chat/channels/{channel.Id}/mark-as-read/{message.Id}"; + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.Method = HttpMethod.Put; + return req; + } + } +} diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 451174a73c..6f67a95f53 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -36,6 +36,11 @@ namespace osu.Game.Online.Chat /// public readonly SortedList Messages = new SortedList(Comparer.Default); + /// + /// Contains all the messages that weren't read by the user. + /// + public IEnumerable UnreadMessages => Messages.Where(m => LastReadId < m.Id); + /// /// Contains all the messages that are still pending for submission to the server. /// @@ -75,6 +80,9 @@ namespace osu.Game.Online.Chat [JsonProperty(@"last_message_id")] public long? LastMessageId; + [JsonProperty(@"last_read_id")] + public long? LastReadId; + /// /// Signalles if the current user joined this channel or not. Defaults to false. /// diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 1d8c5609d9..4b5ec1cad0 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -445,6 +445,28 @@ namespace osu.Game.Online.Chat return tcs.Task; } + /// + /// Marks the as read + /// + /// The channel that will be marked as read + public void MarkChannelAsRead(Channel channel) + { + if (channel.LastMessageId == channel.LastReadId) + return; + + var message = channel.Messages.LastOrDefault(); + + if (message == null) + return; + + var req = new MarkChannelAsReadRequest(channel, message); + + req.Success += () => channel.LastReadId = message.Id; + req.Failure += e => Logger.Error(e, $"Failed to mark channel {channel} up to '{message}' as read"); + + api.Queue(req); + } + [BackgroundDependencyLoader] private void load(IAPIProvider api) { diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 9c48ebd09b..bd4fedabd4 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -12,8 +12,10 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Threading; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; +using osu.Game.Online.Placeholders; using osuTK; using osuTK.Graphics; @@ -150,7 +152,7 @@ namespace osu.Game.Online.Leaderboards break; case PlaceholderState.NotLoggedIn: - replacePlaceholder(new MessagePlaceholder(@"Please sign in to view online leaderboards!")); + replacePlaceholder(new LoginPlaceholder()); break; case PlaceholderState.NotSupporter: @@ -180,10 +182,14 @@ namespace osu.Game.Online.Leaderboards { new Drawable[] { - scrollContainer = new OsuScrollContainer + new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, + Child = scrollContainer = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + } } }, new Drawable[] diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 6ac5219282..9c7324d913 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -11,11 +11,15 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Select; using osu.Game.Scoring; using osu.Game.Users.Drawables; using osuTK; @@ -25,7 +29,7 @@ using osu.Game.Online.API; namespace osu.Game.Online.Leaderboards { - public class LeaderboardScore : OsuClickableContainer + public class LeaderboardScore : OsuClickableContainer, IHasContextMenu { public const float HEIGHT = 60; @@ -51,6 +55,9 @@ namespace osu.Game.Online.Leaderboards private List statisticsLabels; + [Resolved] + private DialogOverlay dialogOverlay { get; set; } + public LeaderboardScore(ScoreInfo score, int rank, bool allowHighlight = true) { this.score = score; @@ -359,5 +366,18 @@ namespace osu.Game.Online.Leaderboards Value = value; } } + + public MenuItem[] ContextMenuItems + { + get + { + List items = new List(); + + if (score.ID != 0) + items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(score)))); + + return items.ToArray(); + } + } } } diff --git a/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs b/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs index 801f3f8ff0..15d7dabe65 100644 --- a/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs +++ b/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; +using osu.Game.Online.Placeholders; using osuTK; namespace osu.Game.Online.Leaderboards diff --git a/osu.Game/Online/Placeholders/LoginPlaceholder.cs b/osu.Game/Online/Placeholders/LoginPlaceholder.cs new file mode 100644 index 0000000000..ffc6623229 --- /dev/null +++ b/osu.Game/Online/Placeholders/LoginPlaceholder.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Overlays; + +namespace osu.Game.Online.Placeholders +{ + public sealed class LoginPlaceholder : Placeholder + { + [Resolved(CanBeNull = true)] + private LoginOverlay login { get; set; } + + public LoginPlaceholder() + { + AddIcon(FontAwesome.Solid.UserLock, cp => + { + cp.Font = cp.Font.With(size: TEXT_SIZE); + cp.Padding = new MarginPadding { Right = 10 }; + }); + + AddText(@"Please sign in to view online leaderboards!"); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + this.ScaleTo(0.8f, 4000, Easing.OutQuint); + return base.OnMouseDown(e); + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + this.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(e); + } + + protected override bool OnClick(ClickEvent e) + { + login?.Show(); + return base.OnClick(e); + } + } +} diff --git a/osu.Game/Online/Leaderboards/MessagePlaceholder.cs b/osu.Game/Online/Placeholders/MessagePlaceholder.cs similarity index 95% rename from osu.Game/Online/Leaderboards/MessagePlaceholder.cs rename to osu.Game/Online/Placeholders/MessagePlaceholder.cs index ef425dacd8..7342765ca4 100644 --- a/osu.Game/Online/Leaderboards/MessagePlaceholder.cs +++ b/osu.Game/Online/Placeholders/MessagePlaceholder.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -namespace osu.Game.Online.Leaderboards +namespace osu.Game.Online.Placeholders { public class MessagePlaceholder : Placeholder { diff --git a/osu.Game/Online/Leaderboards/Placeholder.cs b/osu.Game/Online/Placeholders/Placeholder.cs similarity index 95% rename from osu.Game/Online/Leaderboards/Placeholder.cs rename to osu.Game/Online/Placeholders/Placeholder.cs index d38110a9d0..f58282bbd9 100644 --- a/osu.Game/Online/Leaderboards/Placeholder.cs +++ b/osu.Game/Online/Placeholders/Placeholder.cs @@ -5,7 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; -namespace osu.Game.Online.Leaderboards +namespace osu.Game.Online.Placeholders { public abstract class Placeholder : OsuTextFlowContainer, IEquatable { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 84aba4af52..9df854d178 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -61,6 +61,8 @@ namespace osu.Game private NotificationOverlay notifications; + private NowPlayingOverlay nowPlaying; + private DirectOverlay direct; private SocialOverlay social; @@ -624,7 +626,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(new NowPlayingOverlay + loadComponentSingleFile(nowPlaying = new NowPlayingOverlay { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, @@ -822,6 +824,10 @@ namespace osu.Game switch (action) { + case GlobalAction.ToggleNowPlaying: + nowPlaying.ToggleVisibility(); + return true; + case GlobalAction.ToggleChat: chatOverlay.ToggleVisibility(); return true; diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index e530ff5489..7067e02cd2 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -9,7 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index fe10287491..e0360c6312 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { private readonly bool noVideo; - public string TooltipText => button.Enabled.Value ? "Download this beatmap" : "Login to download"; + public string TooltipText => button.Enabled.Value ? "download this beatmap" : "login to download"; private readonly IBindable localUser = new Bindable(); diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs index a82ff44505..2456568252 100644 --- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs +++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays @@ -13,6 +15,17 @@ namespace osu.Game.Overlays protected override TabControl CreateTabControl() => BreadcrumbControl = new OverlayHeaderBreadcrumbControl(); + protected BreadcrumbControlOverlayHeader(OverlayColourScheme colourScheme) + : base(colourScheme) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BreadcrumbControl.AccentColour = colours.ForOverlayElement(ColourScheme, 1, 0.75f); + } + public class OverlayHeaderBreadcrumbControl : BreadcrumbControl { public OverlayHeaderBreadcrumbControl() diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 7e47a3e29f..d5e0890b4d 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -26,6 +26,7 @@ namespace osu.Game.Overlays.Changelog private const string listing_string = "listing"; public ChangelogHeader() + : base(OverlayColourScheme.Purple) { BreadcrumbControl.AddItem(listing_string); BreadcrumbControl.Current.ValueChanged += e => @@ -43,14 +44,6 @@ namespace osu.Game.Overlays.Changelog }; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BreadcrumbControl.AccentColour = colours.Violet; - TitleBackgroundColour = colours.GreyVioletDarker; - ControlBackgroundColour = colours.GreyVioletDark; - } - private ChangelogHeaderTitle title; private void showBuild(ValueChangedEvent e) @@ -117,12 +110,6 @@ namespace osu.Game.Overlays.Changelog Version = null; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Violet; - } - protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog"); } } diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index 4b1d595b44..2e4d8ce729 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -81,7 +81,10 @@ namespace osu.Game.Overlays.Chat.Tabs RemoveItem(channel); if (Current.Value == channel) - Current.Value = Items.FirstOrDefault(); + { + // Prefer non-selector channels first + Current.Value = Items.FirstOrDefault(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)) ?? Items.FirstOrDefault(); + } } protected override void SelectTab(TabItem tab) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index e80c51d82a..45d311df28 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -279,6 +279,10 @@ namespace osu.Game.Overlays currentChannelContainer.Clear(false); currentChannelContainer.Add(loaded); } + + // mark channel as read when channel switched + if (e.NewValue.Messages.Any()) + channelManager.MarkChannelAsRead(e.NewValue); } private float startDragChatHeight; diff --git a/osu.Game/Overlays/ControllableOverlayHeader.cs b/osu.Game/Overlays/ControllableOverlayHeader.cs index 9b2bf526ca..3117990699 100644 --- a/osu.Game/Overlays/ControllableOverlayHeader.cs +++ b/osu.Game/Overlays/ControllableOverlayHeader.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; using osuTK.Graphics; namespace osu.Game.Overlays @@ -12,14 +14,10 @@ namespace osu.Game.Overlays /// The type of item to be represented by tabs in . public abstract class ControllableOverlayHeader : OverlayHeader { - protected Color4 ControlBackgroundColour - { - set => controlBackground.Colour = value; - } - private readonly Box controlBackground; - protected ControllableOverlayHeader() + protected ControllableOverlayHeader(OverlayColourScheme colourScheme) + : base(colourScheme) { HeaderInfo.Add(new Container { @@ -37,6 +35,12 @@ namespace osu.Game.Overlays }); } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + controlBackground.Colour = colours.ForOverlayElement(ColourScheme, 0.2f, 0.2f); + } + protected abstract TabControl CreateTabControl(); } } diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs index 4037cd46f3..ed44f1e960 100644 --- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs +++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Direct if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) { button.Enabled.Value = false; - button.TooltipText = "This beatmap is currently not available for download."; + button.TooltipText = "this beatmap is currently not available for download."; return; } diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs index 15aec1f17c..aa28b0659d 100644 --- a/osu.Game/Overlays/MedalOverlay.cs +++ b/osu.Game/Overlays/MedalOverlay.cs @@ -21,7 +21,7 @@ using osu.Framework.Graphics.Shapes; using System; using osu.Framework.Graphics.Effects; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; namespace osu.Game.Overlays { diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 69a4a4181a..77ef08f52d 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Mods foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - backgroundIcon.Icon = modAfter.Icon; + backgroundIcon.Mod = modAfter; using (BeginDelayedSequence(mod_switch_duration, true)) { @@ -218,8 +218,8 @@ namespace osu.Game.Overlays.Mods private void displayMod(Mod mod) { if (backgroundIcon != null) - backgroundIcon.Icon = foregroundIcon.Icon; - foregroundIcon.Icon = mod.Icon; + backgroundIcon.Mod = foregroundIcon.Mod; + foregroundIcon.Mod = mod; text.Text = mod.Name; Colour = mod.HasImplementation ? Color4.White : Color4.Gray; } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index bafdad3508..3c0f6468bc 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -8,7 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Input.Bindings; @@ -27,6 +27,11 @@ namespace osu.Game.Overlays public IBindableList BeatmapSets => beatmapSets; + /// + /// Point in time after which the current track will be restarted on triggering a "previous track" action. + /// + private const double restart_cutoff_point = 5000; + private readonly BindableList beatmapSets = new BindableList(); public bool IsUserPaused { get; private set; } @@ -151,11 +156,19 @@ namespace osu.Game.Overlays } /// - /// Play the previous track. + /// Play the previous track or restart the current track if it's current time below /// - /// Whether the operation was successful. - public bool PrevTrack() + /// The that indicate the decided action + public PreviousTrackResult PreviousTrack() { + var currentTrackPosition = current?.Track.CurrentTime; + + if (currentTrackPosition >= restart_cutoff_point) + { + SeekTo(0); + return PreviousTrackResult.Restart; + } + queuedDirection = TrackChangeDirection.Prev; var playable = BeatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? BeatmapSets.LastOrDefault(); @@ -166,10 +179,10 @@ namespace osu.Game.Overlays working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); beatmap.Value.Track.Restart(); - return true; + return PreviousTrackResult.Previous; } - return false; + return PreviousTrackResult.None; } /// @@ -296,8 +309,16 @@ namespace osu.Game.Overlays return true; case GlobalAction.MusicPrev: - if (PrevTrack()) - onScreenDisplay?.Display(new MusicControllerToast("Previous track")); + switch (PreviousTrack()) + { + case PreviousTrackResult.Restart: + onScreenDisplay?.Display(new MusicControllerToast("Restart track")); + break; + + case PreviousTrackResult.Previous: + onScreenDisplay?.Display(new MusicControllerToast("Previous track")); + break; + } return true; } @@ -322,4 +343,11 @@ namespace osu.Game.Overlays Next, Prev } + + public enum PreviousTrackResult + { + None, + Restart, + Previous + } } diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs index e484309a18..f61b30b381 100644 --- a/osu.Game/Overlays/News/NewsArticleCover.cs +++ b/osu.Game/Overlays/News/NewsArticleCover.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK.Graphics; @@ -19,6 +20,10 @@ namespace osu.Game.Overlays.News { public class NewsArticleCover : Container { + private const int hover_duration = 300; + + private readonly Box gradient; + public NewsArticleCover(ArticleInfo info) { RelativeSizeAxes = Axes.X; @@ -47,11 +52,11 @@ namespace osu.Game.Overlays.News Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, }, - new Box + gradient = new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.6f)), - Alpha = 1f, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.7f)), + Alpha = 0 }, new DateContainer(info.Time) { @@ -90,6 +95,18 @@ namespace osu.Game.Overlays.News bg.OnLoadComplete += d => d.FadeIn(250, Easing.In); } + protected override bool OnHover(HoverEvent e) + { + gradient.FadeIn(hover_duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + gradient.FadeOut(hover_duration, Easing.OutQuint); + } + [LongRunningLoad] private class NewsBackground : Sprite { diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index fc88c86df2..03dc64b3bd 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -23,6 +23,7 @@ namespace osu.Game.Overlays.News public Action ShowFrontPage; public NewsHeader() + : base(OverlayColourScheme.Purple) { BreadcrumbControl.AddItem(front_page_string); @@ -35,14 +36,6 @@ namespace osu.Game.Overlays.News Current.ValueChanged += showPost; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BreadcrumbControl.AccentColour = colours.Violet; - TitleBackgroundColour = colours.GreyVioletDarker; - ControlBackgroundColour = colours.GreyVioletDark; - } - private void showPost(ValueChangedEvent e) { if (e.OldValue != null) @@ -97,12 +90,6 @@ namespace osu.Game.Overlays.News } protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/news"); - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Violet; - } } } } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index de30e1a754..a8ba7fa427 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -137,7 +137,7 @@ namespace osu.Game.Overlays { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = () => musicController.PrevTrack(), + Action = () => musicController.PreviousTrack(), Icon = FontAwesome.Solid.StepBackward, }, playButton = new MusicIconButton diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index 8c36e8cc9b..353e14c20d 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osuTK.Graphics; @@ -14,20 +16,21 @@ namespace osu.Game.Overlays { private readonly Box titleBackground; private readonly Container background; - protected readonly FillFlowContainer HeaderInfo; + private readonly ScreenTitle title; - protected Color4 TitleBackgroundColour - { - set => titleBackground.Colour = value; - } + protected readonly FillFlowContainer HeaderInfo; protected float BackgroundHeight { set => background.Height = value; } - protected OverlayHeader() + protected OverlayColourScheme ColourScheme { get; } + + protected OverlayHeader(OverlayColourScheme colourScheme) { + ColourScheme = colourScheme; + RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -64,7 +67,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = Color4.Gray, }, - CreateTitle().With(title => + title = CreateTitle().With(title => { title.Margin = new MarginPadding { @@ -81,6 +84,13 @@ namespace osu.Game.Overlays }); } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + titleBackground.Colour = colours.ForOverlayElement(ColourScheme, 0.2f, 0.15f); + title.AccentColour = colours.ForOverlayElement(ColourScheme, 1, 0.7f); + } + protected abstract Drawable CreateBackground(); [NotNull] diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs index 8069937810..29471375b5 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public LevelBadge() { - TooltipText = "Level"; + TooltipText = "level"; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index 6a6532764f..a73ce56a2b 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public LevelProgressBar() { - TooltipText = "Progress to next level"; + TooltipText = "progress to next level"; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 768344dfee..2ad852a29f 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -25,6 +24,7 @@ namespace osu.Game.Overlays.Profile private DetailHeaderContainer detailHeaderContainer; public ProfileHeader() + : base(OverlayColourScheme.Green) { BackgroundHeight = 150; @@ -36,14 +36,6 @@ namespace osu.Game.Overlays.Profile centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - TabControl.AccentColour = colours.Seafoam; - TitleBackgroundColour = colours.GreySeafoamDarker; - ControlBackgroundColour = colours.GreySeafoam; - } - protected override Drawable CreateBackground() => new Container { @@ -109,12 +101,6 @@ namespace osu.Game.Overlays.Profile Section = "info"; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Seafoam; - } - protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/profile"); } } diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs new file mode 100644 index 0000000000..2b12457ccc --- /dev/null +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -0,0 +1,105 @@ +// 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.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Overlays.Rankings +{ + public class CountryFilter : CompositeDrawable, IHasCurrentValue + { + private const int duration = 200; + private const int height = 50; + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + private readonly Box background; + private readonly CountryPill countryPill; + private readonly Container content; + + public CountryFilter() + { + RelativeSizeAxes = Axes.X; + + InternalChild = content = new Container + { + RelativeSizeAxes = Axes.X, + Height = height, + Alpha = 0, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = @"filtered by country:", + Font = OsuFont.GetFont(size: 14) + }, + countryPill = new CountryPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Alpha = 0, + Current = Current + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoam; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(onCountryChanged, true); + } + + private void onCountryChanged(ValueChangedEvent country) + { + if (country.NewValue == null) + { + countryPill.Collapse(); + this.ResizeHeightTo(0, duration, Easing.OutQuint); + content.FadeOut(duration, Easing.OutQuint); + return; + } + + this.ResizeHeightTo(height, duration, Easing.OutQuint); + content.FadeIn(duration, Easing.OutQuint); + countryPill.Expand(); + } + } +} diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs new file mode 100644 index 0000000000..410d316006 --- /dev/null +++ b/osu.Game/Overlays/Rankings/CountryPill.cs @@ -0,0 +1,164 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Rankings +{ + public class CountryPill : CompositeDrawable, IHasCurrentValue + { + private const int duration = 200; + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + private readonly Container content; + private readonly Box background; + private readonly UpdateableFlag flag; + private readonly OsuSpriteText countryName; + + public CountryPill() + { + AutoSizeAxes = Axes.Both; + + InternalChild = content = new CircularContainer + { + Height = 25, + AutoSizeDuration = duration, + AutoSizeEasing = Easing.OutQuint, + Masking = true, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding { Horizontal = 10 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(8, 0), + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + flag = new UpdateableFlag + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(22, 15) + }, + countryName = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 14) + } + } + }, + new CloseButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => Current.Value = null + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.GreySeafoamDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(onCountryChanged, true); + } + + public void Expand() + { + content.ClearTransforms(); + content.AutoSizeAxes = Axes.X; + + this.FadeIn(duration, Easing.OutQuint); + } + + public void Collapse() + { + content.ClearTransforms(); + content.AutoSizeAxes = Axes.None; + content.ResizeWidthTo(0, duration, Easing.OutQuint); + + this.FadeOut(duration, Easing.OutQuint); + } + + private void onCountryChanged(ValueChangedEvent country) + { + if (country.NewValue == null) + return; + + flag.Country = country.NewValue; + countryName.Text = country.NewValue.FullName; + } + + private class CloseButton : OsuHoverContainer + { + private readonly SpriteIcon icon; + + protected override IEnumerable EffectTargets => new[] { icon }; + + public CloseButton() + { + AutoSizeAxes = Axes.Both; + Add(icon = new SpriteIcon + { + Size = new Vector2(8), + Icon = FontAwesome.Solid.Times + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IdleColour = colours.GreySeafoamLighter; + HoverColour = Color4.White; + } + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 27796c1e32..e485aa5ea9 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -323,8 +323,6 @@ namespace osu.Game.Overlays.Settings.Sections.General Colour = Color4.Black.Opacity(0.25f), Radius = 4, }; - - ItemsContainer.Padding = new MarginPadding(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs index 55c7210d6c..db6f24a954 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input new SettingsButton { Text = "Key configuration", - TooltipText = "Change global shortcut keys and gameplay bindings", + TooltipText = "change global shortcut keys and gameplay bindings", Action = keyConfig.ToggleVisibility }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 4f2f3dfd1a..59d39a1c3c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class SensitivitySlider : OsuSliderBar { - public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : $"{base.TooltipText}x"; + public override string TooltipText => Current.Disabled ? "enable raw input to adjust sensitivity" : $"{base.TooltipText}x"; } } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 35f28ab1b2..e89f2adf0b 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -161,11 +161,7 @@ namespace osu.Game.Overlays.Settings UpdateState(); } - public string TooltipText => "Revert to default"; - - protected override bool OnMouseDown(MouseDownEvent e) => true; - - protected override bool OnMouseUp(MouseUpEvent e) => true; + public string TooltipText => "revert to default"; protected override bool OnClick(ClickEvent e) { diff --git a/osu.Game/Overlays/TabControlOverlayHeader.cs b/osu.Game/Overlays/TabControlOverlayHeader.cs index d108af4348..a4bf423e46 100644 --- a/osu.Game/Overlays/TabControlOverlayHeader.cs +++ b/osu.Game/Overlays/TabControlOverlayHeader.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; @@ -16,6 +17,17 @@ namespace osu.Game.Overlays protected override TabControl CreateTabControl() => TabControl = new OverlayHeaderTabControl(); + protected TabControlOverlayHeader(OverlayColourScheme colourScheme) + : base(colourScheme) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + TabControl.AccentColour = colours.ForOverlayElement(ColourScheme, 1, 0.75f); + } + public class OverlayHeaderTabControl : OverlayTabControl { public OverlayHeaderTabControl() diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index da696e0fdd..7effd290e6 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b780ec9e76..e168171186 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mods /// The icon of this mod. /// [JsonIgnore] - public virtual IconUsage Icon => FontAwesome.Solid.Question; + public virtual IconUsage? Icon => null; /// /// The type of this mod. diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 070a10b1c8..e51b8b6457 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Autoplay"; public override string Acronym => "AT"; - public override IconUsage Icon => OsuIcon.ModAuto; + public override IconUsage? Icon => OsuIcon.ModAuto; public override ModType Type => ModType.Automation; public override string Description => "Watch a perfect automated play through the song."; public override double ScoreMultiplier => 1; diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index 3487d49e08..cd08aee453 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Cinema"; public override string Acronym => "CN"; - public override IconUsage Icon => OsuIcon.ModCinema; + public override IconUsage? Icon => OsuIcon.ModCinema; public override string Description => "Watch the video without visual distractions."; public void ApplyToHUD(HUDOverlay overlay) diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 71a666414f..bd98e735e5 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Daycore"; public override string Acronym => "DC"; - public override IconUsage Icon => FontAwesome.Solid.Question; + public override IconUsage? Icon => null; public override string Description => "Whoaaaaa..."; private readonly BindableNumber tempoAdjust = new BindableDouble(1); diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index c5b8a1bc73..b74bf01d1b 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Conversion; - public override IconUsage Icon => FontAwesome.Solid.Hammer; + public override IconUsage? Icon => FontAwesome.Solid.Hammer; public override double ScoreMultiplier => 1.0; diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index 7015460c51..152657da33 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Double Time"; public override string Acronym => "DT"; - public override IconUsage Icon => OsuIcon.ModDoubletime; + public override IconUsage? Icon => OsuIcon.ModDoubletime; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Zoooooooooom..."; public override bool Ranked => true; diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index ec0f50c0be..b56be95dfe 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Easy"; public override string Acronym => "EZ"; - public override IconUsage Icon => OsuIcon.ModEasy; + public override IconUsage? Icon => OsuIcon.ModEasy; public override ModType Type => ModType.DifficultyReduction; public override double ScoreMultiplier => 0.5; public override bool Ranked => true; diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 4f939362bb..35a8334237 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Flashlight"; public override string Acronym => "FL"; - public override IconUsage Icon => OsuIcon.ModFlashlight; + public override IconUsage? Icon => OsuIcon.ModFlashlight; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Restricted view area."; public override bool Ranked => true; diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 15f7afa312..203b88951c 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Half Time"; public override string Acronym => "HT"; - public override IconUsage Icon => OsuIcon.ModHalftime; + public override IconUsage? Icon => OsuIcon.ModHalftime; public override ModType Type => ModType.DifficultyReduction; public override string Description => "Less zoom..."; public override bool Ranked => true; diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index a613d41cf4..58c9a58408 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Hard Rock"; public override string Acronym => "HR"; - public override IconUsage Icon => OsuIcon.ModHardrock; + public override IconUsage? Icon => OsuIcon.ModHardrock; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Everything just got a bit harder..."; public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) }; diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 0934992f55..4e4a75db82 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Hidden"; public override string Acronym => "HD"; - public override IconUsage Icon => OsuIcon.ModHidden; + public override IconUsage? Icon => OsuIcon.ModHidden; public override ModType Type => ModType.DifficultyIncrease; public override bool Ranked => true; diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index a8c79bb896..1df2aeb348 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Nightcore"; public override string Acronym => "NC"; - public override IconUsage Icon => OsuIcon.ModNightcore; + public override IconUsage? Icon => OsuIcon.ModNightcore; public override string Description => "Uguuuuuuuu..."; private readonly BindableNumber tempoAdjust = new BindableDouble(1); diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 49ee3354c3..b95ec7490e 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "No Fail"; public override string Acronym => "NF"; - public override IconUsage Icon => OsuIcon.ModNofail; + public override IconUsage? Icon => OsuIcon.ModNofail; public override ModType Type => ModType.DifficultyReduction; public override string Description => "You can't fail, no matter what."; public override double ScoreMultiplier => 0.5; diff --git a/osu.Game/Rulesets/Mods/ModNoMod.cs b/osu.Game/Rulesets/Mods/ModNoMod.cs index 487985b2b3..379a2122f2 100644 --- a/osu.Game/Rulesets/Mods/ModNoMod.cs +++ b/osu.Game/Rulesets/Mods/ModNoMod.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "No Mod"; public override string Acronym => "NM"; public override double ScoreMultiplier => 1; - public override IconUsage Icon => FontAwesome.Solid.Ban; + public override IconUsage? Icon => FontAwesome.Solid.Ban; public override ModType Type => ModType.System; } } diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index afa263f1c9..882d3ebd6a 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Perfect"; public override string Acronym => "PF"; - public override IconUsage Icon => OsuIcon.ModPerfect; + public override IconUsage? Icon => OsuIcon.ModPerfect; public override string Description => "SS or quit."; protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => result.Type != result.Judgement.MaxResult; diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs index 7c355577d4..b6fec42f43 100644 --- a/osu.Game/Rulesets/Mods/ModRelax.cs +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Relax"; public override string Acronym => "RX"; - public override IconUsage Icon => OsuIcon.ModRelax; + public override IconUsage? Icon => OsuIcon.ModRelax; public override ModType Type => ModType.Automation; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) }; diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index a4d0631d8c..8799431f1d 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Sudden Death"; public override string Acronym => "SD"; - public override IconUsage Icon => OsuIcon.ModSuddendeath; + public override IconUsage? Icon => OsuIcon.ModSuddendeath; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Miss and fail."; public override double ScoreMultiplier => 1; diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index 5416f1ac22..da3bd75b44 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Wind Down"; public override string Acronym => "WD"; public override string Description => "Sloooow doooown..."; - public override IconUsage Icon => FontAwesome.Solid.ChevronCircleDown; + public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown; public override double ScoreMultiplier => 1.0; [SettingSource("Final rate", "The speed increase to ramp towards")] diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index 3cf584f3dd..3f456a42a5 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Wind Up"; public override string Acronym => "WU"; public override string Description => "Can you keep up?"; - public override IconUsage Icon => FontAwesome.Solid.ChevronCircleUp; + public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp; public override double ScoreMultiplier => 1.0; [SettingSource("Final rate", "The speed increase to ramp towards")] diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index 99672240e2..5588e9c0b7 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 3eab4555d1..1fc51d2ce8 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -10,7 +10,7 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Audio; using System.Linq; using JetBrains.Annotations; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets.Objects.Legacy diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 293138097f..62a5b6f0b5 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -7,7 +7,7 @@ using System.Linq; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Caching; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Types; using osuTK; diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index 0c6b3f67b4..45edc0f4a3 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -3,7 +3,7 @@ using System; using osu.Framework.Bindables; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; namespace osu.Game.Rulesets.Scoring diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 945dbe4cc9..3edab0745d 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osuTK; using osu.Framework.Bindables; @@ -20,25 +21,30 @@ namespace osu.Game.Rulesets.UI public readonly BindableBool Selected = new BindableBool(); private readonly SpriteIcon modIcon; + private readonly SpriteText modAcronym; private readonly SpriteIcon background; private const float size = 80; - public IconUsage Icon - { - get => modIcon.Icon; - set => modIcon.Icon = value; - } - private readonly ModType type; public virtual string TooltipText { get; } - protected Mod Mod { get; private set; } + private Mod mod; + + public Mod Mod + { + get => mod; + set + { + mod = value; + updateMod(value); + } + } public ModIcon(Mod mod) { - Mod = mod ?? throw new ArgumentNullException(nameof(mod)); + this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); type = mod.Type; @@ -56,15 +62,43 @@ namespace osu.Game.Rulesets.UI Icon = OsuIcon.ModBg, Shadow = true, }, + modAcronym = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Colour = OsuColour.Gray(84), + Alpha = 0, + Font = OsuFont.Numeric.With(null, 22f), + UseFullGlyphHeight = false, + Text = mod.Acronym + }, modIcon = new SpriteIcon { Origin = Anchor.Centre, Anchor = Anchor.Centre, Colour = OsuColour.Gray(84), - Size = new Vector2(size - 35), - Icon = mod.Icon + Size = new Vector2(45), + Icon = FontAwesome.Solid.Question }, }; + + updateMod(mod); + } + + private void updateMod(Mod value) + { + modAcronym.Text = value.Acronym; + modIcon.Icon = value.Icon ?? FontAwesome.Solid.Question; + + if (value.Icon is null) + { + modIcon.FadeOut(); + modAcronym.FadeIn(); + return; + } + + modIcon.FadeIn(); + modAcronym.FadeOut(); } private Color4 backgroundColour; diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 1ab3a5b533..3ced9ee753 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -49,9 +49,12 @@ namespace osu.Game.Screens.Backgrounds Beatmap = beatmap; InternalChild = dimmable = CreateFadeContainer(); + dimmable.EnableUserDim.BindTo(EnableUserDim); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); + + StoryboardReplacesBackground.BindTo(dimmable.StoryboardReplacesBackground); } [BackgroundDependencyLoader] @@ -99,7 +102,6 @@ namespace osu.Game.Screens.Backgrounds b.Depth = newDepth; dimmable.Background = Background = b; - StoryboardReplacesBackground.BindTo(dimmable.StoryboardReplacesBackground); } public override bool Equals(BackgroundScreen other) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 095985e9d1..980a127cf4 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -4,7 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Configuration; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs index 54922fec5e..9aa527667b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Transforms; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics.Containers; using osuTK; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 14d69cddd1..e212b429b9 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -44,6 +44,9 @@ namespace osu.Game.Screens.Edit public override bool DisallowExternalBeatmapRulesetChanges => true; + [Resolved] + private BeatmapManager beatmapManager { get; set; } + private Box bottomBackground; private Container screenContainer; @@ -56,7 +59,6 @@ namespace osu.Game.Screens.Edit private EditorBeatmap editorBeatmap; private DependencyContainer dependencies; - private GameHost host; protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo); @@ -66,8 +68,6 @@ namespace osu.Game.Screens.Edit [BackgroundDependencyLoader] private void load(OsuColour colours, GameHost host) { - this.host = host; - beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor; beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); @@ -90,7 +90,8 @@ namespace osu.Game.Screens.Edit if (RuntimeInfo.IsDesktop) { - fileMenuItems.Add(new EditorMenuItem("Export", MenuItemType.Standard, exportBeatmap)); + fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap)); + fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); fileMenuItems.Add(new EditorMenuItemSpacer()); } @@ -205,6 +206,15 @@ namespace osu.Game.Screens.Edit case Key.Right: seek(e, 1); return true; + + case Key.S: + if (e.ControlPressed) + { + saveBeatmap(); + return true; + } + + break; } return base.OnKeyDown(e); @@ -292,8 +302,6 @@ namespace osu.Game.Screens.Edit } } - private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save()); - private void onModeChanged(ValueChangedEvent e) { currentScreen?.Exit(); @@ -329,5 +337,13 @@ namespace osu.Game.Screens.Edit else clock.SeekForward(!clock.IsRunning, amount); } + + private void saveBeatmap() => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap); + + private void exportBeatmap() + { + saveBeatmap(); + beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); + } } } diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 93a5f19121..e5e47507f3 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -3,7 +3,7 @@ using System; using System.Linq; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 41ee01be20..289413c65a 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -6,7 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shaders; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Screens.Menu; using osuTK; using osu.Framework.Screens; diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index df83e98494..26455b1dbd 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -7,7 +7,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Configuration; diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index c86f1393a4..50cfe23481 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Timing; using osu.Game.Graphics; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 1a625f8d83..8fc07f5989 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -19,6 +19,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Utils; namespace osu.Game.Screens.Menu { @@ -205,13 +206,13 @@ namespace osu.Game.Screens.Menu if (audioData[i] < amplitude_dead_zone) continue; - float rotation = MathHelper.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); + float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); float rotationCos = MathF.Cos(rotation); float rotationSin = MathF.Sin(rotation); //taking the cos and sin to the 0..1 range var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; - var barSize = new Vector2(size * (float)Math.Sqrt(2 * (1 - Math.Cos(MathHelper.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); + var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); //The distance between the position and the sides of the bar. var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); //The distance between the bottom side of the bar and the top side. diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 534400e720..33b6ee8025 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs index a3caffb620..54c644c999 100644 --- a/osu.Game/Screens/Play/FailAnimation.cs +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -10,7 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Graphics; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; using osuTK; diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 640224c057..7946e6d322 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 64fcc48004..f37faac988 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Play private LogoTrackingContainer content; - private BeatmapMetadataDisplay info; + protected BeatmapMetadataDisplay MetadataInfo; private bool hideOverlays; public override bool HideOverlaysOnEnter => hideOverlays; @@ -96,7 +96,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, }).WithChildren(new Drawable[] { - info = new BeatmapMetadataDisplay(Beatmap.Value, Mods.Value, content.LogoFacade) + MetadataInfo = new BeatmapMetadataDisplay(Beatmap.Value, Mods, content.LogoFacade) { Alpha = 0, Anchor = Anchor.Centre, @@ -138,7 +138,7 @@ namespace osu.Game.Screens.Play contentIn(); - info.Delay(750).FadeIn(500); + MetadataInfo.Delay(750).FadeIn(500); this.Delay(1800).Schedule(pushWhenLoaded); if (!muteWarningShownOnce.Value) @@ -158,7 +158,7 @@ namespace osu.Game.Screens.Play contentIn(); - info.Loading = true; + MetadataInfo.Loading = true; //we will only be resumed if the player has requested a re-run (see ValidForResume setting above) loadNewPlayer(); @@ -174,7 +174,7 @@ namespace osu.Game.Screens.Play player.RestartCount = restartCount; player.RestartRequested = restartRequested; - LoadTask = LoadComponentAsync(player, _ => info.Loading = false); + LoadTask = LoadComponentAsync(player, _ => MetadataInfo.Loading = false); } private void contentIn() @@ -350,7 +350,7 @@ namespace osu.Game.Screens.Play } } - private class BeatmapMetadataDisplay : Container + protected class BeatmapMetadataDisplay : Container { private class MetadataLine : Container { @@ -379,11 +379,13 @@ namespace osu.Game.Screens.Play } private readonly WorkingBeatmap beatmap; - private readonly IReadOnlyList mods; + private readonly Bindable> mods; private readonly Drawable facade; private LoadingAnimation loading; private Sprite backgroundSprite; + public IBindable> Mods => mods; + public bool Loading { set @@ -401,11 +403,13 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, IReadOnlyList mods, Drawable facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable facade) { this.beatmap = beatmap; - this.mods = mods; this.facade = facade; + + this.mods = new Bindable>(); + this.mods.BindTo(mods); } [BackgroundDependencyLoader] @@ -492,7 +496,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Top = 20 }, - Current = { Value = mods } + Current = mods } }, } diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 1a5ed20953..772d326c7f 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -19,7 +19,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs index cdf495e257..9df36c9c2b 100644 --- a/osu.Game/Screens/Play/SongProgressBar.cs +++ b/osu.Game/Screens/Play/SongProgressBar.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Framework.Threading; namespace osu.Game.Screens.Play diff --git a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs index 9cc6ea2628..62213720aa 100644 --- a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs @@ -74,15 +74,15 @@ namespace osu.Game.Screens.Ranking.Pages switch (replayAvailability) { case ReplayAvailability.Local: - button.TooltipText = @"Watch replay"; + button.TooltipText = @"watch replay"; break; case ReplayAvailability.Online: - button.TooltipText = @"Download replay"; + button.TooltipText = @"download replay"; break; default: - button.TooltipText = @"Replay unavailable"; + button.TooltipText = @"replay unavailable"; break; } }, true); diff --git a/osu.Game/Screens/Ranking/Pages/RetryButton.cs b/osu.Game/Screens/Ranking/Pages/RetryButton.cs index 2a281224c1..06d0440b30 100644 --- a/osu.Game/Screens/Ranking/Pages/RetryButton.cs +++ b/osu.Game/Screens/Ranking/Pages/RetryButton.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Ranking.Pages }, }; - TooltipText = "Retry"; + TooltipText = "retry"; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 4acc619753..4433543ca1 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -9,7 +9,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Configuration; using osuTK.Input; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -412,6 +412,12 @@ namespace osu.Game.Screens.Select protected override bool OnKeyDown(KeyDownEvent e) { + // allow for controlling volume when alt is held. + // this is required as the VolumeControlReceptor uses OnPressed, which is + // executed after all OnKeyDown events. + if (e.AltPressed) + return base.OnKeyDown(e); + int direction = 0; bool skipDifficulties = false; diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs index c9b6ca7bb3..b32416b361 100644 --- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs +++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs @@ -14,7 +14,8 @@ namespace osu.Game.Screens.Select { public class BeatmapClearScoresDialog : PopupDialog { - private ScoreManager scoreManager; + [Resolved] + private ScoreManager scoreManager { get; set; } public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action onCompletion) { @@ -38,11 +39,5 @@ namespace osu.Game.Screens.Select }, }; } - - [BackgroundDependencyLoader] - private void load(ScoreManager scoreManager) - { - this.scoreManager = scoreManager; - } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index d54c13c7db..451708c1cf 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -13,7 +13,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index 6118191302..121491d6ca 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Graphics; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index a147527f6c..b7f60a8370 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using System; using osu.Game.Beatmaps; using osu.Framework.Bindables; using System.Collections.Generic; @@ -118,17 +117,9 @@ namespace osu.Game.Screens.Select.Details mod.ApplyToDifficulty(adjustedDifficulty); } - //mania specific - if ((Beatmap?.Ruleset?.ID ?? 0) == 3) - { - firstValue.Title = "Key Amount"; - firstValue.Value = ((int)MathF.Round(baseDifficulty?.CircleSize ?? 0), (int)MathF.Round(adjustedDifficulty?.CircleSize ?? 0)); - } - else - { - firstValue.Title = "Circle Size"; - firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); - } + // Account for mania differences + firstValue.Title = (Beatmap?.Ruleset?.ID ?? 0) == 3 ? "Key Amount" : "Circle Size"; + firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); starDifficulty.Value = ((float)(Beatmap?.StarDifficulty ?? 0), null); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 1b45a9d270..e36493c82f 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -103,6 +103,8 @@ namespace osu.Game.Screens.Select.Leaderboards { ScoreSelected = s => ScoreSelected?.Invoke(s) }); + + scoreManager.ItemRemoved += onScoreRemoved; } protected override void Reset() @@ -111,6 +113,8 @@ namespace osu.Game.Screens.Select.Leaderboards TopScore = null; } + private void onScoreRemoved(ScoreInfo score) => Schedule(RefreshScores); + protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; protected override APIRequest FetchScores(Action> scoresCallback) @@ -186,5 +190,13 @@ namespace osu.Game.Screens.Select.Leaderboards { Action = () => ScoreSelected?.Invoke(model) }; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (scoreManager != null) + scoreManager.ItemRemoved -= onScoreRemoved; + } } } diff --git a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs new file mode 100644 index 0000000000..97df40fa6d --- /dev/null +++ b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs @@ -0,0 +1,54 @@ +// 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.Game.Overlays.Dialog; +using osu.Game.Scoring; +using System.Diagnostics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Select +{ + public class LocalScoreDeleteDialog : PopupDialog + { + private readonly ScoreInfo score; + + [Resolved] + private ScoreManager scoreManager { get; set; } + + [Resolved] + private BeatmapManager beatmapManager { get; set; } + + public LocalScoreDeleteDialog(ScoreInfo score) + { + this.score = score; + Debug.Assert(score != null); + } + + [BackgroundDependencyLoader] + private void load() + { + BeatmapInfo beatmap = beatmapManager.QueryBeatmap(b => b.ID == score.BeatmapInfoID); + Debug.Assert(beatmap != null); + + string accuracy = string.Format(score.Accuracy == 1 ? "{0:P0}" : "{0:P2}", score.Accuracy); + BodyText = $"{score.User} ({accuracy}, {score.Rank})"; + + Icon = FontAwesome.Regular.TrashAlt; + HeaderText = "Confirm deletion of local score"; + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = "Yes. Please.", + Action = () => scoreManager?.Delete(score) + }, + new PopupDialogCancelButton + { + Text = "No, I'm still attached.", + }, + }; + } + } +} diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index a076bb54df..ced3b9c1b6 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -8,7 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Textures; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; namespace osu.Game.Storyboards.Drawables diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index ac795b3349..c0da0e9c0e 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -8,7 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.MathUtils; +using osu.Framework.Utils; using osu.Game.Beatmaps; namespace osu.Game.Storyboards.Drawables diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs index f76cba7f41..5412b11b33 100644 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ b/osu.Game/Updater/SimpleUpdateManager.cs @@ -7,8 +7,8 @@ using Newtonsoft.Json; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; -using osu.Framework.IO.Network; using osu.Framework.Platform; +using osu.Game.Online.API; using osu.Game.Overlays.Notifications; namespace osu.Game.Updater @@ -36,7 +36,7 @@ namespace osu.Game.Updater { try { - var releases = new JsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest"); + var releases = new OsuJsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest"); await releases.PerformAsync(); diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index ee9af15863..93136e88a0 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -74,7 +74,7 @@ namespace osu.Game.Users.Drawables private class ClickableArea : OsuClickableContainer { - public override string TooltipText => Enabled.Value ? @"View Profile" : null; + public override string TooltipText => Enabled.Value ? @"view profile" : null; protected override bool OnClick(ClickEvent e) { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b497133e62..f41c5fee7f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index edd35b0774..bb4a59a601 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + diff --git a/osu.sln b/osu.sln index 79823848f0..1d64f6ff10 100644 --- a/osu.sln +++ b/osu.sln @@ -65,6 +65,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution osu.TestProject.props = osu.TestProject.props EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Benchmarks", "osu.Game.Benchmarks\osu.Game.Benchmarks.csproj", "{93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -399,6 +401,18 @@ Global {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Debug|iPhone.Build.0 = Debug|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|Any CPU.Build.0 = Release|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhone.ActiveCfg = Release|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhone.Build.0 = Release|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE