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/Gemfile.lock b/Gemfile.lock index ab594aee74..e3954c2681 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.1) + CFPropertyList (3.0.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) atomos (0.1.3) @@ -18,8 +18,8 @@ GEM unf (>= 0.0.5, < 1.0.0) dotenv (2.7.5) emoji_regex (1.0.1) - excon (0.67.0) - faraday (0.15.4) + excon (0.71.1) + faraday (0.17.3) multipart-post (>= 1.2, < 3) faraday-cookie_jar (0.0.6) faraday (>= 0.7.4) @@ -27,7 +27,7 @@ GEM faraday_middleware (0.13.1) faraday (>= 0.7.4, < 1.0) fastimage (2.1.7) - fastlane (2.133.0) + fastlane (2.140.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) babosa (>= 1.0.2, < 2.0.0) @@ -36,13 +36,13 @@ GEM commander-fastlane (>= 4.4.6, < 5.0.0) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 2.0) - excon (>= 0.45.0, < 1.0.0) - faraday (< 0.16.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 0.17) faraday-cookie_jar (~> 0.0.6) - faraday_middleware (< 0.16.0) + faraday_middleware (~> 0.13.1) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.21.2, < 0.24.0) + google-api-client (>= 0.29.2, < 0.37.0) google-cloud-storage (>= 1.15.0, < 2.0.0) highline (>= 1.7.2, < 2.0.0) json (< 3.0.0) @@ -61,56 +61,58 @@ GEM tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) - xcodeproj (>= 1.8.1, < 2.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) fastlane-plugin-clean_testflight_testers (0.3.0) - fastlane-plugin-souyuz (0.8.1) - souyuz (>= 0.8.1) + fastlane-plugin-souyuz (0.9.1) + souyuz (= 0.9.1) fastlane-plugin-xamarin (0.6.3) gh_inspector (1.1.3) - google-api-client (0.23.9) + google-api-client (0.36.4) addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.5, < 0.7.0) + googleauth (~> 0.9) httpclient (>= 2.8.1, < 3.0) - mime-types (~> 3.0) + mini_mime (~> 1.0) representable (~> 3.0) retriable (>= 2.0, < 4.0) - signet (~> 0.9) - google-cloud-core (1.3.1) + signet (~> 0.12) + google-cloud-core (1.5.0) google-cloud-env (~> 1.0) - google-cloud-env (1.2.1) + google-cloud-errors (~> 1.0) + google-cloud-env (1.3.0) faraday (~> 0.11) - google-cloud-storage (1.16.0) + google-cloud-errors (1.0.0) + google-cloud-storage (1.25.1) + addressable (~> 2.5) digest-crc (~> 0.4) - google-api-client (~> 0.23) + google-api-client (~> 0.33) google-cloud-core (~> 1.2) - googleauth (>= 0.6.2, < 0.10.0) - googleauth (0.6.7) + googleauth (~> 0.9) + mini_mime (~> 1.0) + googleauth (0.10.0) faraday (~> 0.12) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.7) + signet (~> 0.12) highline (1.7.10) http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) - json (2.2.0) + json (2.3.0) jwt (2.1.0) - memoist (0.16.0) - mime-types (3.3) - mime-types-data (~> 3.2015) - mime-types-data (3.2019.1009) - mini_magick (4.9.5) + memoist (0.16.2) + mini_magick (4.10.1) + mini_mime (1.0.2) mini_portile2 (2.4.0) - multi_json (1.13.1) + multi_json (1.14.1) multi_xml (0.6.0) multipart-post (2.0.0) nanaimo (0.2.6) naturally (2.2.0) - nokogiri (1.10.4) + nokogiri (1.10.7) mini_portile2 (~> 2.4.0) os (1.0.1) plist (3.5.0) @@ -128,12 +130,12 @@ GEM faraday (~> 0.9) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.6) + simctl (1.6.7) CFPropertyList naturally slack-notifier (2.3.2) - souyuz (0.8.1) - fastlane (>= 2.29.0) + souyuz (0.9.1) + fastlane (>= 1.103.0) highline (~> 1.7) nokogiri (~> 1.7) terminal-notifier (2.0.0) @@ -141,15 +143,15 @@ GEM unicode-display_width (~> 1.1, >= 1.1.1) tty-cursor (0.7.0) tty-screen (0.7.0) - tty-spinner (0.9.1) + tty-spinner (0.9.2) tty-cursor (~> 0.7) uber (0.1.0) unf (0.1.4) unf_ext unf_ext (0.0.7.6) - unicode-display_width (1.6.0) + unicode-display_width (1.6.1) word_wrap (1.0.0) - xcodeproj (1.12.0) + xcodeproj (1.14.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) 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..2ccba60424 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/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index a47efcc10a..4c72b9fd3e 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -36,7 +36,10 @@ namespace osu.Game.Rulesets.Catch.Mods //disable keyboard controls public bool OnPressed(CatchAction action) => true; - public bool OnReleased(CatchAction action) => true; + + public void OnReleased(CatchAction action) + { + } protected override bool OnMouseMove(MouseMoveEvent e) { 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..1de0b6bfa3 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; @@ -103,7 +103,9 @@ namespace osu.Game.Rulesets.Catch.UI MovableCatcher.X = state.CatcherX.Value; } - public bool OnReleased(CatchAction action) => false; + public void OnReleased(CatchAction action) + { + } public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); @@ -341,24 +343,22 @@ namespace osu.Game.Rulesets.Catch.UI return false; } - public bool OnReleased(CatchAction action) + public void OnReleased(CatchAction action) { switch (action) { case CatchAction.MoveLeft: currentDirection++; - return true; + break; case CatchAction.MoveRight: currentDirection--; - return true; + break; case CatchAction.Dash: Dashing = false; - return true; + break; } - - return false; } /// 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/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index b28d8bb0e6..7a3b42914e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { EndPlacement(); - return base.OnMouseUp(e); + base.OnMouseUp(e); } public override void UpdatePosition(Vector2 screenSpacePosition) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs index 3bd7fb2d49..ff3dbe614a 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -55,14 +55,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return base.OnMouseDown(e); } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { - var result = base.OnDrag(e); + base.OnDrag(e); ScreenSpaceDragPosition = e.ScreenSpaceMousePosition; DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition); - - return result; } public override void Show() diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs new file mode 100644 index 0000000000..5f66ae7491 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs @@ -0,0 +1,34 @@ +// 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.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania.Edit.Blueprints; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Screens.Edit.Compose.Components; + +namespace osu.Game.Rulesets.Mania.Edit +{ + public class ManiaBlueprintContainer : ComposeBlueprintContainer + { + public ManiaBlueprintContainer(IEnumerable drawableHitObjects) + : base(drawableHitObjects) + { + } + + public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) + { + switch (hitObject) + { + case DrawableNote note: + return new NoteSelectionBlueprint(note); + + case DrawableHoldNote holdNote: + return new HoldNoteSelectionBlueprint(holdNote); + } + + return base.CreateBlueprintFor(hitObject); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 1632b6a583..62b609610f 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -5,11 +5,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mania.Objects.Drawables; -using osu.Game.Rulesets.Objects.Drawables; using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -52,26 +49,12 @@ namespace osu.Game.Rulesets.Mania.Edit return drawableRuleset; } + protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects); + protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { new NoteCompositionTool(), new HoldNoteCompositionTool() }; - - public override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler(); - - public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) - { - switch (hitObject) - { - case DrawableNote note: - return new NoteSelectionBlueprint(note); - - case DrawableHoldNote holdNote: - return new HoldNoteSelectionBlueprint(holdNote); - } - - return base.CreateBlueprintFor(hitObject); - } } } 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/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 155adb958b..14a7c5fda3 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -171,17 +171,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables bodyPiece.Hitting = true; } - public bool OnReleased(ManiaAction action) + public void OnReleased(ManiaAction action) { if (AllJudged) - return false; + return; if (action != Action.Value) - return false; + return; // Make sure a hold was started if (HoldStartTime == null) - return false; + return; Tail.UpdateResult(); endHold(); @@ -189,8 +189,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // If the key has been released too early, the user should not receive full score for the release if (!Tail.IsHit) HasBroken = true; - - return true; } private void endHold() diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs index a5d03bf765..390c64c5e2 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note - public override bool OnReleased(ManiaAction action) => false; // Handled by the hold note + public override void OnReleased(ManiaAction action) + { + } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs index a660144dd1..568b07c958 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs @@ -59,6 +59,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note - public override bool OnReleased(ManiaAction action) => false; // Handled by the hold note + public override void OnReleased(ManiaAction action) + { + } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 8f353ae138..85613d3afb 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -77,6 +77,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables return UpdateResult(true); } - public virtual bool OnReleased(ManiaAction action) => false; + public virtual void OnReleased(ManiaAction action) + { + } } } diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 3d2a070b0f..63c573d344 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -191,7 +191,9 @@ namespace osu.Game.Rulesets.Mania.UI return true; } - public bool OnReleased(ManiaAction action) => false; + public void OnReleased(ManiaAction action) + { + } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) // This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs index 57241da564..75cc351310 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs @@ -98,11 +98,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components return false; } - public bool OnReleased(ManiaAction action) + public void OnReleased(ManiaAction action) { if (action == this.action.Value) backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); - return false; } } } 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/Components/ColumnKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs index 85880222d7..60fc2713b3 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs @@ -115,11 +115,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components return false; } - public bool OnReleased(ManiaAction action) + public void OnReleased(ManiaAction action) { if (action == this.action.Value) keyIcon.ScaleTo(1f, 125, Easing.OutQuint); - return false; } } } 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/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 6a0730db91..af4da5e853 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -135,13 +135,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return false; } - protected override bool OnMouseUp(MouseUpEvent e) => RequestSelection != null; - protected override bool OnClick(ClickEvent e) => RequestSelection != null; protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left; - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { if (ControlPoint == slider.Path.ControlPoints[0]) { @@ -158,12 +156,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } else ControlPoint.Position.Value += e.Delta; - - return true; } - protected override bool OnDragEnd(DragEndEvent e) => true; - /// /// Updates the state of the circular control point marker. /// diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 6f583d7983..e293eba9d7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -108,7 +108,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return false; } - public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete; + public void OnReleased(PlatformAction action) + { + } private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e) { diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 2497e428fc..90512849d4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -106,11 +106,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { if (state == PlacementState.Body && e.Button == MouseButton.Right) endCurve(); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnDoubleClick(DoubleClickEvent e) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 3165c441fb..4fdead512a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -90,19 +90,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnDragStart(DragStartEvent e) => placementControlPointIndex != null; - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { Debug.Assert(placementControlPointIndex != null); HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position; - - return true; } - protected override bool OnDragEnd(DragEndEvent e) + protected override void OnDragEnd(DragEndEvent e) { placementControlPointIndex = null; - return true; } private BindableList controlPoints => HitObject.Path.ControlPoints; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs new file mode 100644 index 0000000000..30682616e6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; +using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders; +using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Screens.Edit.Compose.Components; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuBlueprintContainer : ComposeBlueprintContainer + { + public OsuBlueprintContainer(IEnumerable drawableHitObjects) + : base(drawableHitObjects) + { + } + + protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler(); + + public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) + { + switch (hitObject) + { + case DrawableHitCircle circle: + return new HitCircleSelectionBlueprint(circle); + + case DrawableSlider slider: + return new SliderSelectionBlueprint(slider); + + case DrawableSpinner spinner: + return new SpinnerSelectionBlueprint(spinner); + } + + return base.CreateBlueprintFor(hitObject); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 49624ea733..b01488e7c2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -9,12 +9,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; -using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders; -using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; @@ -37,24 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit new SpinnerCompositionTool() }; - public override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler(); - - public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) - { - switch (hitObject) - { - case DrawableHitCircle circle: - return new HitCircleSelectionBlueprint(circle); - - case DrawableSlider slider: - return new SliderSelectionBlueprint(slider); - - case DrawableSpinner spinner: - return new SpinnerSelectionBlueprint(spinner); - } - - return base.CreateBlueprintFor(hitObject); - } + protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects); protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable selectedHitObjects) { 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/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index f74f2d7bc5..3162f4b379 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -205,7 +205,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return false; } - public bool OnReleased(OsuAction action) => false; + public void OnReleased(OsuAction action) + { + } } } } 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.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 6433ced624..a463542e64 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor return false; } - public bool OnReleased(OsuAction action) + public void OnReleased(OsuAction action) { switch (action) { @@ -120,8 +120,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor updateExpandedState(); break; } - - return false; } public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index 3b18e41f30..ca3030db3e 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -107,7 +107,9 @@ namespace osu.Game.Rulesets.Osu.UI return false; } - public bool OnReleased(OsuAction action) => false; + public void OnReleased(OsuAction action) + { + } public void Appear() => Schedule(() => { 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.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 4b25ff0ecc..85dfc8d5e0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -77,11 +77,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return result; } - public override bool OnReleased(TaikoAction action) + public override void OnReleased(TaikoAction action) { if (action == HitAction) HitAction = null; - return base.OnReleased(action); + + base.OnReleased(action); } protected override void Update() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index b9d31ff906..5f892dd2fa 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -77,7 +77,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public Drawable CreateProxiedContent() => proxiedContent.CreateProxy(); public abstract bool OnPressed(TaikoAction action); - public virtual bool OnReleased(TaikoAction action) => false; + + public virtual void OnReleased(TaikoAction action) + { + } public override double LifetimeStart { diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 5234ae1f69..d26ccfe867 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -187,7 +187,9 @@ namespace osu.Game.Rulesets.Taiko.UI return false; } - public bool OnReleased(TaikoAction action) => false; + public void OnReleased(TaikoAction action) + { + } } } } 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/Editor/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs index 2d336bd19c..e825df5a3f 100644 --- a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Edit; @@ -19,7 +20,13 @@ namespace osu.Game.Tests.Editor private TestHitObjectComposer composer; [Cached(typeof(EditorBeatmap))] - private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + [Cached(typeof(IBeatSnapProvider))] + private readonly EditorBeatmap editorBeatmap; + + public TestSceneHitObjectComposerDistanceSnapping() + { + editorBeatmap = new EditorBeatmap(new OsuBeatmap(), BeatDivisor); + } [SetUp] public void Setup() => Schedule(() => 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/Online/TestAPIModSerialization.cs b/osu.Game.Tests/Online/TestAPIModSerialization.cs new file mode 100644 index 0000000000..d9318aa822 --- /dev/null +++ b/osu.Game.Tests/Online/TestAPIModSerialization.cs @@ -0,0 +1,82 @@ +// 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 Newtonsoft.Json; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Tests.Online +{ + [TestFixture] + public class TestAPIModSerialization + { + [Test] + public void TestAcronymIsPreserved() + { + var apiMod = new APIMod(new TestMod()); + + var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); + + Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym)); + } + + [Test] + public void TestRawSettingIsPreserved() + { + var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } }); + + var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); + + Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0)); + } + + [Test] + public void TestConvertedModHasCorrectSetting() + { + var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } }); + + var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); + var converted = (TestMod)deserialized.ToMod(new TestRuleset()); + + Assert.That(converted.TestSetting.Value, Is.EqualTo(2)); + } + + private class TestRuleset : Ruleset + { + public override IEnumerable GetModsFor(ModType type) => new[] { new TestMod() }; + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new System.NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException(); + + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException(); + + public override string Description { get; } = string.Empty; + public override string ShortName { get; } = string.Empty; + } + + private class TestMod : Mod + { + public override string Name => "Test Mod"; + public override string Acronym => "TM"; + public override double ScoreMultiplier => 1; + + [SettingSource("Test")] + public BindableNumber TestSetting { get; } = new BindableDouble + { + MinValue = 0, + MaxValue = 10, + Default = 5, + Precision = 0.01, + }; + } + } +} diff --git a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs index 3562689482..a8830824c0 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; @@ -14,6 +15,7 @@ namespace osu.Game.Tests.Visual.Editor public class TestSceneComposeScreen : EditorClockTestScene { [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap { diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs index 29575cb42e..e9372bd134 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs @@ -40,6 +40,8 @@ namespace osu.Game.Tests.Visual.Editor var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap); + Dependencies.Cache(editorBeatmap); + Children = new Drawable[] { new FillFlowContainer diff --git a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs index c001c83877..e41c2427fb 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs @@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.Editor Dependencies.CacheAs(clock); Dependencies.CacheAs(clock); Dependencies.CacheAs(editorBeatmap); + Dependencies.CacheAs(editorBeatmap); Child = new OsuHitObjectComposer(new OsuRuleset()); } 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/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 8dcb7dcbf8..e2ce2341e5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -25,6 +25,8 @@ namespace osu.Game.Tests.Visual.UserInterface private readonly Mod testCustomisableMod = new TestModCustomisable1(); + private readonly Mod testCustomisableAutoOpenMod = new TestModCustomisable2(); + [Test] public void TestButtonShowsOnCustomisableMod() { @@ -53,6 +55,17 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); } + [Test] + public void TestCustomisationOpensOnModSelect() + { + createModSelect(); + + AddStep("open", () => modSelect.Show()); + AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0); + AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); + AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.Alpha == 1); + } + private void createModSelect() { AddStep("create mod select", () => @@ -128,6 +141,8 @@ namespace osu.Game.Tests.Visual.UserInterface public override string Name => "Customisable Mod 2"; public override string Acronym => "CM2"; + + public override bool RequiresConfiguration => true; } private abstract class TestModCustomisable : Mod, IApplicableMod 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/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/Screens/Ladder/Components/DrawableTournamentMatch.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs index dde280ccd8..c4b670f059 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs @@ -289,16 +289,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return true; } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { - if (base.OnDrag(e)) return true; + base.OnDrag(e); Selected = true; this.MoveToOffset(e.Delta); var pos = Position; Match.Position.Value = new Point((int)pos.X, (int)pos.Y); - return true; } public void Remove() diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index 84a329085a..cb73985b11 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -10,8 +10,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class ProgressionPath : Path { - public DrawableTournamentMatch Source { get; private set; } - public DrawableTournamentMatch Destination { get; private set; } + public DrawableTournamentMatch Source { get; } + public DrawableTournamentMatch Destination { get; } public ProgressionPath(DrawableTournamentMatch source, DrawableTournamentMatch destination) { diff --git a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs index 0c450a66b4..bdaa1ae7fd 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs @@ -22,10 +22,9 @@ namespace osu.Game.Tournament.Screens.Ladder protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { this.MoveTo(target += e.Delta, 1000, Easing.OutQuint); - return true; } private const float min_scale = 0.6f; 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..1c94856a4e 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); } } @@ -296,7 +303,7 @@ namespace osu.Game.Tournament private class TournamentInputManager : UserInputManager { - protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button) + protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button) { switch (button) { @@ -304,7 +311,7 @@ namespace osu.Game.Tournament return new RightMouseManager(button); } - return base.CreateButtonManagerFor(button); + return base.CreateButtonEventManagerFor(button); } private class RightMouseManager : MouseButtonEventManager diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 66c07209f3..2406b0bef2 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -19,6 +19,6 @@ namespace osu.Game.Audio public IEnumerable LookupNames => new[] { sampleName }; - public int Volume { get; set; } = 100; + public int Volume { get; } = 100; } } 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/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 056fa8bcc0..f859dccc80 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -35,16 +35,11 @@ namespace osu.Game.Configuration { public static IEnumerable CreateSettingsControls(this object obj) { - foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) + foreach (var (attr, property) in obj.GetSettingsSourceProperties()) { - var attr = property.GetCustomAttribute(true); + object value = property.GetValue(obj); - if (attr == null) - continue; - - var prop = property.GetValue(obj); - - switch (prop) + switch (value) { case BindableNumber bNumber: yield return new SettingsSlider @@ -102,9 +97,22 @@ namespace osu.Game.Configuration break; default: - throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})"); + throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({value})"); } } } + + public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj) + { + foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) + { + var attr = property.GetCustomAttribute(true); + + if (attr == null) + continue; + + yield return (attr, property); + } + } } } 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/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index facf70b47a..93ac69bdbf 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -76,12 +76,12 @@ namespace osu.Game.Graphics.Containers return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) Hide(); - return base.OnMouseUp(e); + base.OnMouseUp(e); } public virtual bool OnPressed(GlobalAction action) @@ -99,7 +99,9 @@ namespace osu.Game.Graphics.Containers return false; } - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } protected override void UpdateState(ValueChangedEvent state) { diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index cfd459da5e..6d531887ed 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -50,15 +50,15 @@ namespace osu.Game.Graphics.Containers return base.OnMouseDown(e); } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { if (rightMouseDragging) { scrollFromMouseEvent(e); - return true; + return; } - return base.OnDrag(e); + base.OnDrag(e); } protected override bool OnDragStart(DragStartEvent e) @@ -72,15 +72,15 @@ namespace osu.Game.Graphics.Containers return base.OnDragStart(e); } - protected override bool OnDragEnd(DragEndEvent e) + protected override void OnDragEnd(DragEndEvent e) { if (rightMouseDragging) { rightMouseDragging = false; - return true; + return; } - return base.OnDragEnd(e); + base.OnDragEnd(e); } protected override bool OnScroll(ScrollEvent e) @@ -162,13 +162,13 @@ namespace osu.Game.Graphics.Containers return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { - if (e.Button != MouseButton.Left) return false; + if (e.Button != MouseButton.Left) return; box.FadeColour(Color4.White, 100); - return base.OnMouseUp(e); + base.OnMouseUp(e); } } } 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..580177d17a 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; @@ -91,7 +92,7 @@ namespace osu.Game.Graphics.Cursor return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { if (!e.IsPressed(MouseButton.Left) && !e.IsPressed(MouseButton.Right)) { @@ -106,7 +107,7 @@ namespace osu.Game.Graphics.Cursor dragRotationState = DragRotationState.NotDragging; } - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override void PopIn() diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 2dc12b3e67..53a40f5613 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 @@ -35,6 +36,20 @@ namespace osu.Game.Graphics Convert.ToByte(hex.Substring(2, 2), 16), Convert.ToByte(hex.Substring(4, 2), 16), 255); + + case 4: + return new Color4( + (byte)(Convert.ToByte(hex.Substring(0, 1), 16) * 17), + (byte)(Convert.ToByte(hex.Substring(1, 1), 16) * 17), + (byte)(Convert.ToByte(hex.Substring(2, 1), 16) * 17), + (byte)(Convert.ToByte(hex.Substring(3, 1), 16) * 17)); + + case 8: + return new Color4( + Convert.ToByte(hex.Substring(0, 2), 16), + Convert.ToByte(hex.Substring(2, 2), 16), + Convert.ToByte(hex.Substring(4, 2), 16), + Convert.ToByte(hex.Substring(6, 2), 16)); } } @@ -63,6 +78,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 +220,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/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index b9151b7393..7f20c30048 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -67,7 +67,9 @@ namespace osu.Game.Graphics return false; } - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } private volatile int screenShotTasks; diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index 23565e8742..88ba7ede6e 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -67,7 +67,9 @@ namespace osu.Game.Graphics.UserInterface return false; } - public bool OnReleased(GlobalAction action) => action == GlobalAction.Back; + public void OnReleased(GlobalAction action) + { + } } } } diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index aed07e56ee..9b53ee7b2d 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -232,11 +232,11 @@ namespace osu.Game.Graphics.UserInterface return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { if (Selected.Value) colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnHover(HoverEvent e) 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/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 0b183c0ec9..e59353a480 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -80,7 +80,9 @@ namespace osu.Game.Graphics.UserInterface return false; } - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } public override bool RequestsFocus => HoldFocus; } diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index fcd8940348..40899e7e95 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -24,7 +24,7 @@ namespace osu.Game.Graphics.UserInterface /// /// Length of debounce for hover sound playback, in milliseconds. Default is 50ms. /// - public double HoverDebounceTime { get; set; } = 50; + public double HoverDebounceTime { get; } = 50; protected readonly HoverSampleSet SampleSet; diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs index 660bd7979f..cfcf034d1c 100644 --- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -107,10 +107,10 @@ namespace osu.Game.Graphics.UserInterface return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { Content.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); + base.OnMouseUp(e); } } } diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index c6a9aa1c97..9cf8f02024 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -129,10 +129,10 @@ namespace osu.Game.Graphics.UserInterface return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { Content.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected virtual SpriteText CreateText() => new OsuSpriteText 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/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 958390d5d2..2112aac6a3 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -128,10 +128,10 @@ namespace osu.Game.Graphics.UserInterface return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { Nub.Current.Value = false; - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override void OnUserChange(T value) 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/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 24d8009f40..01d8edaecf 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -16,11 +16,7 @@ namespace osu.Game.Graphics.UserInterface /// /// How many leading zeroes the counter has. /// - public uint LeadingZeroes - { - get; - protected set; - } + public uint LeadingZeroes { get; } /// /// Displays score. 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/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index 39ccf9fe1c..63a6348b57 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -50,7 +50,7 @@ namespace osu.Game.Input public bool OnPressed(PlatformAction action) => updateLastInteractionTime(); - public bool OnReleased(PlatformAction action) => updateLastInteractionTime(); + public void OnReleased(PlatformAction action) => updateLastInteractionTime(); protected override bool Handle(UIEvent e) { diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs new file mode 100644 index 0000000000..46a8db31b7 --- /dev/null +++ b/osu.Game/Online/API/APIMod.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using Humanizer; +using Newtonsoft.Json; +using osu.Framework.Bindables; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Online.API +{ + public class APIMod : IMod + { + [JsonProperty("acronym")] + public string Acronym { get; set; } + + [JsonProperty("settings")] + public Dictionary Settings { get; set; } = new Dictionary(); + + [JsonConstructor] + private APIMod() + { + } + + public APIMod(Mod mod) + { + Acronym = mod.Acronym; + + foreach (var (_, property) in mod.GetSettingsSourceProperties()) + Settings.Add(property.Name.Underscore(), property.GetValue(mod)); + } + + public Mod ToMod(Ruleset ruleset) + { + Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym); + + if (resultMod == null) + throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); + + foreach (var (_, property) in resultMod.GetSettingsSourceProperties()) + { + if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue)) + continue; + + ((IBindable)property.GetValue(resultMod)).Parse(settingValue); + } + + return resultMod; + } + + public bool Equals(IMod other) => Acronym == other?.Acronym; + } +} 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/API/Requests/Responses/APIMod.cs b/osu.Game/Online/API/Requests/Responses/APIMod.cs deleted file mode 100644 index b9da4f49ee..0000000000 --- a/osu.Game/Online/API/Requests/Responses/APIMod.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Online.API.Requests.Responses -{ - public class APIMod : IMod - { - public string Acronym { get; set; } - - public bool Equals(IMod other) => Acronym == other?.Acronym; - } -} 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..d109f28e72 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 @@ -54,10 +55,10 @@ namespace osu.Game.Online.Leaderboards return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { icon.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); + base.OnMouseUp(e); } } } diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index d13e8b31e6..5f8edc607b 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using osu.Game.Beatmaps; +using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -50,7 +51,7 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("allowed_mods")] private APIMod[] allowedMods { - get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray(); + get => AllowedMods.Select(m => new APIMod(m)).ToArray(); set => allowedModsBacking = value; } @@ -59,7 +60,7 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("required_mods")] private APIMod[] requiredMods { - get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray(); + get => RequiredMods.Select(m => new APIMod(m)).ToArray(); set => requiredModsBacking = value; } @@ -72,10 +73,12 @@ namespace osu.Game.Online.Multiplayer Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets); Ruleset = rulesets.GetRuleset(RulesetID); + Ruleset rulesetInstance = Ruleset.CreateInstance(); + if (allowedModsBacking != null) { AllowedMods.Clear(); - AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => allowedModsBacking.Any(m => m.Acronym == mod.Acronym))); + AllowedMods.AddRange(allowedModsBacking.Select(m => m.ToMod(rulesetInstance))); allowedModsBacking = null; } @@ -83,7 +86,7 @@ namespace osu.Game.Online.Multiplayer if (requiredModsBacking != null) { RequiredMods.Clear(); - RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => requiredModsBacking.Any(m => m.Acronym == mod.Acronym))); + RequiredMods.AddRange(requiredModsBacking.Select(m => m.ToMod(rulesetInstance))); requiredModsBacking = null; } diff --git a/osu.Game/Online/Placeholders/LoginPlaceholder.cs b/osu.Game/Online/Placeholders/LoginPlaceholder.cs new file mode 100644 index 0000000000..591eb976e2 --- /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 void OnMouseUp(MouseUpEvent e) + { + this.ScaleTo(1, 1000, Easing.OutElastic); + 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..ff46c6d849 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; @@ -875,7 +881,9 @@ namespace osu.Game #endregion - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } private Container overlayContent; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0819642d2d..07c9d37a86 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -330,7 +330,7 @@ namespace osu.Game private class OsuUserInputManager : UserInputManager { - protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button) + protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button) { switch (button) { @@ -338,7 +338,7 @@ namespace osu.Game return new RightMouseManager(button); } - return base.CreateButtonManagerFor(button); + return base.CreateButtonEventManagerFor(button); } private class RightMouseManager : MouseButtonEventManager 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 8a82b1f0c0..2e50c19729 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/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 266e68f17e..09dc06b95f 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -141,16 +141,13 @@ namespace osu.Game.Overlays.Chat.Tabs updateState(); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { switch (e.Button) { case MouseButton.Middle: CloseButton.Click(); - return true; - - default: - return false; + break; } } diff --git a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs index bde930d4fb..178afda5ac 100644 --- a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs +++ b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs @@ -34,10 +34,10 @@ namespace osu.Game.Overlays.Chat.Tabs return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { icon.ScaleTo(0.75f, 1000, Easing.OutElastic); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index e80c51d82a..2c0fa49b5d 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; @@ -295,7 +299,7 @@ namespace osu.Game.Overlays return true; } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { if (isDragging) { @@ -307,14 +311,12 @@ namespace osu.Game.Overlays ChatHeight.Value = targetChatHeight; } - - return true; } - protected override bool OnDragEnd(DragEndEvent e) + protected override void OnDragEnd(DragEndEvent e) { isDragging = false; - return base.OnDragEnd(e); + base.OnDragEnd(e); } private void selectTab(int index) 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/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 8317951c8a..d2fcc2652a 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -177,17 +177,19 @@ namespace osu.Game.Overlays.KeyBinding return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { // don't do anything until the last button is released. if (!HasFocus || e.HasAnyButtonPressed) - return base.OnMouseUp(e); + { + base.OnMouseUp(e); + return; + } if (bindTarget.IsHovered) finalise(); else updateBindTarget(); - return true; } protected override bool OnScroll(ScrollEvent e) @@ -216,12 +218,15 @@ namespace osu.Game.Overlays.KeyBinding return true; } - protected override bool OnKeyUp(KeyUpEvent e) + protected override void OnKeyUp(KeyUpEvent e) { - if (!HasFocus) return base.OnKeyUp(e); + if (!HasFocus) + { + base.OnKeyUp(e); + return; + } finalise(); - return true; } protected override bool OnJoystickPress(JoystickPressEvent e) @@ -235,13 +240,15 @@ namespace osu.Game.Overlays.KeyBinding return true; } - protected override bool OnJoystickRelease(JoystickReleaseEvent e) + protected override void OnJoystickRelease(JoystickReleaseEvent e) { if (!HasFocus) - return base.OnJoystickRelease(e); + { + base.OnJoystickRelease(e); + return; + } finalise(); - return true; } private void clear() @@ -313,14 +320,6 @@ namespace osu.Game.Overlays.KeyBinding Size = new Vector2(80, 20); } - protected override bool OnMouseUp(MouseUpEvent e) - { - base.OnMouseUp(e); - - // without this, the mouse up triggers a finalise (and deselection) of the current binding target. - return true; - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { 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..e574828cd2 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)) { @@ -158,7 +158,7 @@ namespace osu.Game.Overlays.Mods return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { scaleContainer.ScaleTo(1, 500, Easing.OutElastic); @@ -172,8 +172,6 @@ namespace osu.Game.Overlays.Mods break; } } - - return true; } protected override bool OnClick(ClickEvent e) @@ -218,8 +216,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/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 7f07ce620c..38f5d54714 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -473,7 +473,10 @@ namespace osu.Game.Overlays.Mods if (selectedMod != null) { if (State.Value == Visibility.Visible) sampleOn?.Play(); + DeselectTypes(selectedMod.IncompatibleMods, true); + + if (selectedMod.RequiresConfiguration) ModSettingsContainer.Alpha = 1; } else { diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 29b6ae00f3..d40f391982 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -43,10 +43,10 @@ namespace osu.Game.Overlays.Music return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { IsDraggable = false; - return base.OnMouseUp(e); + base.OnMouseUp(e); } private bool selected; diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 3cd04ac809..7bdcab6dff 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -136,29 +136,29 @@ namespace osu.Game.Overlays.Music return draggedItem != null || base.OnDragStart(e); } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { nativeDragPosition = e.ScreenSpaceMousePosition; - if (draggedItem == null) - return base.OnDrag(e); - return true; + if (draggedItem == null) + base.OnDrag(e); } - protected override bool OnDragEnd(DragEndEvent e) + protected override void OnDragEnd(DragEndEvent e) { nativeDragPosition = e.ScreenSpaceMousePosition; if (draggedItem == null) - return base.OnDragEnd(e); + { + base.OnDragEnd(e); + return; + } if (dragDestination != null) musicController.ChangeBeatmapSetPosition(draggedItem.BeatmapSetInfo, dragDestination.Value); draggedItem = null; dragDestination = null; - - return true; } protected override void Update() diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index bafdad3508..19f06e99f1 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; } @@ -305,7 +326,9 @@ namespace osu.Game.Overlays return false; } - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } public class MusicControllerToast : Toast { @@ -322,4 +345,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..042e95c6d7 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 @@ -385,7 +385,7 @@ namespace osu.Game.Overlays return true; } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { Vector2 change = e.MousePosition - e.MouseDownPosition; @@ -393,13 +393,12 @@ namespace osu.Game.Overlays change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.7f) / change.Length; this.MoveTo(change); - return true; } - protected override bool OnDragEnd(DragEndEvent e) + protected override void OnDragEnd(DragEndEvent e) { this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); - return base.OnDragEnd(e); + base.OnDragEnd(e); } } diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index 53da2da634..c9547bb5b8 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -2,10 +2,12 @@ // 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.Framework.Graphics.UserInterface; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osuTK.Graphics; @@ -16,24 +18,19 @@ namespace osu.Game.Overlays private readonly Box titleBackground; private readonly Box controlBackground; private readonly Container background; - - protected Color4 TitleBackgroundColour - { - set => titleBackground.Colour = value; - } - - protected Color4 ControlBackgroundColour - { - set => controlBackground.Colour = value; - } + private readonly ScreenTitle title; 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; @@ -60,9 +57,8 @@ namespace osu.Game.Overlays titleBackground = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Gray, }, - CreateTitle().With(title => + title = CreateTitle().With(title => { title.Margin = new MarginPadding { @@ -92,6 +88,14 @@ 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); + controlBackground.Colour = colours.ForOverlayElement(ColourScheme, 0.2f, 0.2f); + } + 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 59e64dfc26..b550d7d823 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 f3521b66c8..8f3aa896ee 100644 --- a/osu.Game/Overlays/TabControlOverlayHeader.cs +++ b/osu.Game/Overlays/TabControlOverlayHeader.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; @@ -15,6 +16,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/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index 9cd3aac2cb..76fad945cc 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -16,6 +16,9 @@ namespace osu.Game.Overlays.Volume public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false; public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false; - public bool OnReleased(GlobalAction action) => false; + + public void OnReleased(GlobalAction action) + { + } } } 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/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index bfaa7e8872..9ee3bacf9b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -46,12 +46,12 @@ namespace osu.Game.Rulesets.Edit private IAdjustableClock adjustableClock { get; set; } [Resolved] - private BindableBeatDivisor beatDivisor { get; set; } + private IBeatSnapProvider beatSnapProvider { get; set; } private IBeatmapProcessor beatmapProcessor; private DrawableEditRulesetWrapper drawableRulesetWrapper; - private BlueprintContainer blueprintContainer; + private ComposeBlueprintContainer blueprintContainer; private Container distanceSnapGridContainer; private DistanceSnapGrid distanceSnapGrid; private readonly List layerContainers = new List(); @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Edit new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both } }); - var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChild(blueprintContainer = new BlueprintContainer()); + var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChild(blueprintContainer = CreateBlueprintContainer()); layerContainers.Add(layerBelowRuleset); layerContainers.Add(layerAboveRuleset); @@ -233,6 +233,8 @@ namespace osu.Game.Rulesets.Edit protected abstract IReadOnlyList CompositionTools { get; } + protected abstract ComposeBlueprintContainer CreateBlueprintContainer(); + protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null); public void BeginPlacement(HitObject hitObject) @@ -257,40 +259,26 @@ namespace osu.Game.Rulesets.Edit public override float GetBeatSnapDistanceAt(double referenceTime) { DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime); - return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatDivisor.Value); + return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatSnapProvider.BeatDivisor); } public override float DurationToDistance(double referenceTime, double duration) { - double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; + double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime); return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime)); } public override double DistanceToDuration(double referenceTime, float distance) { - double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; + double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime); return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength; } public override double GetSnappedDurationFromDistance(double referenceTime, float distance) - => beatSnap(referenceTime, DistanceToDuration(referenceTime, distance)); + => beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance)); public override float GetSnappedDistanceFromDistance(double referenceTime, float distance) - => DurationToDistance(referenceTime, beatSnap(referenceTime, DistanceToDuration(referenceTime, distance))); - - /// - /// Snaps a duration to the closest beat of a timing point applicable at the reference time. - /// - /// The time of the timing point which resides in. - /// The duration to snap. - /// A value that represents snapped to the closest beat of the timing point. - private double beatSnap(double referenceTime, double duration) - { - double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value; - - // A 1ms offset prevents rounding errors due to minute variations in duration - return (int)((duration + 1) / beatLength) * beatLength; - } + => DurationToDistance(referenceTime, beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance))); protected override void Dispose(bool isDisposing) { @@ -323,17 +311,6 @@ namespace osu.Game.Rulesets.Edit /// public abstract bool CursorInPlacementArea { get; } - /// - /// Creates a for a specific . - /// - /// The to create the overlay for. - public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null; - - /// - /// Creates a which outlines s and handles movement of selections. - /// - public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler(); - /// /// Creates the applicable for a selection. /// diff --git a/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs b/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs new file mode 100644 index 0000000000..e1daafaebe --- /dev/null +++ b/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Edit +{ + public interface IBeatSnapProvider + { + /// + /// Snaps a duration to the closest beat of a timing point applicable at the reference time. + /// + /// The time of the timing point which resides in. + /// The duration to snap. + /// A value that represents snapped to the closest beat of the timing point. + double SnapTime(double referenceTime, double duration); + + /// + /// Get the most appropriate beat length at a given time. + /// + /// A reference time used for lookup. + /// The most appropriate beat length. + double GetBeatLengthAtTime(double referenceTime); + + /// + /// Returns the current beat divisor. + /// + int BeatDivisor { get; } + } +} diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b780ec9e76..46c0c1da07 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. @@ -60,6 +60,12 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool Ranked => false; + /// + /// Whether this mod requires configuration to apply changes to the game. + /// + [JsonIgnore] + public virtual bool RequiresConfiguration => false; + /// /// The mods this mod cannot be enabled with. /// 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..d74e2ce2bc 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -20,10 +20,12 @@ 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; + public override bool RequiresConfiguration => true; + public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModHardRock) }; [SettingSource("Drain Rate", "Override a beatmap's set HP.")] 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/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 5cc213be41..41b2739fc5 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -139,7 +139,11 @@ namespace osu.Game.Rulesets.UI public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.Rate >= 0)); - public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.Rate >= 0)); + public void OnReleased(T action) + { + foreach (var c in Target.Children.OfType>()) + c.OnReleased(action, Clock.Rate >= 0); + } } #endregion diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index fda1d7c723..8bcdfff2fd 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -201,7 +201,9 @@ namespace osu.Game.Rulesets.UI.Scrolling throw new ArgumentException($"{nameof(Playfield)} must be a {nameof(ScrollingPlayfield)} when using {nameof(DrawableScrollingRuleset)}."); } - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } private class LocalScrollingInfo : IScrollingInfo { 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/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 79ada40a89..5d638d7919 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -32,12 +32,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts } protected override bool OnDragStart(DragStartEvent e) => true; - protected override bool OnDragEnd(DragEndEvent e) => true; - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { seekToPosition(e.ScreenSpaceMousePosition); - return true; } protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index 7706e33179..119635ccd5 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts /// /// Represents a part of the summary timeline.. /// - public abstract class TimelinePart : Container + public class TimelinePart : Container { protected readonly IBindable Beatmap = new Bindable(); @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts protected override Container Content => timeline; - protected TimelinePart() + public TimelinePart() { AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both }); diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 42773ef687..8201ec2710 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -262,10 +262,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { marker.Active = false; - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnClick(ClickEvent e) @@ -274,10 +274,9 @@ namespace osu.Game.Screens.Edit.Compose.Components return true; } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { handleMouseInput(e.ScreenSpaceMousePosition); - return true; } private void handleMouseInput(Vector2 screenSpaceMousePosition) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index cafaddc39e..ce24eb579b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -14,7 +14,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -22,27 +21,30 @@ using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components { - public class BlueprintContainer : CompositeDrawable, IKeyBindingHandler + /// + /// A container which provides a "blueprint" display of hitobjects. + /// Includes selection and manipulation support via a . + /// + public abstract class BlueprintContainer : CompositeDrawable, IKeyBindingHandler { public event Action> SelectionChanged; - private DragBox dragBox; + protected DragBox DragBox { get; private set; } + private SelectionBlueprintContainer selectionBlueprints; - private Container placementBlueprintContainer; - private PlacementBlueprint currentPlacement; + private SelectionHandler selectionHandler; - private InputManager inputManager; [Resolved] private IAdjustableClock adjustableClock { get; set; } - [Resolved] - private HitObjectComposer composer { get; set; } - [Resolved] private EditorBeatmap beatmap { get; set; } - public BlueprintContainer() + [Resolved(canBeNull: true)] + private IDistanceSnapProvider snapProvider { get; set; } + + protected BlueprintContainer() { RelativeSizeAxes = Axes.Both; } @@ -50,50 +52,41 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { - selectionHandler = composer.CreateSelectionHandler(); + selectionHandler = CreateSelectionHandler(); selectionHandler.DeselectAll = deselectAll; - InternalChildren = new[] + AddRangeInternal(new[] { - dragBox = new DragBox(select), + DragBox = CreateDragBox(select), selectionHandler, selectionBlueprints = new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }, - placementBlueprintContainer = new Container { RelativeSizeAxes = Axes.Both }, - dragBox.CreateProxy() - }; + DragBox.CreateProxy().With(p => p.Depth = float.MinValue) + }); - foreach (var obj in composer.HitObjects) - addBlueprintFor(obj); + foreach (var obj in beatmap.HitObjects) + AddBlueprintFor(obj); } protected override void LoadComplete() { base.LoadComplete(); - beatmap.HitObjectAdded += addBlueprintFor; + beatmap.HitObjectAdded += AddBlueprintFor; beatmap.HitObjectRemoved += removeBlueprintFor; - - inputManager = GetContainingInputManager(); } - private HitObjectCompositionTool currentTool; - /// - /// The current placement tool. + /// Creates a which outlines s and handles movement of selections. /// - public HitObjectCompositionTool CurrentTool - { - get => currentTool; - set - { - if (currentTool == value) - return; + protected virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler(); - currentTool = value; + /// + /// Creates a for a specific . + /// + /// The to create the overlay for. + protected virtual SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => null; - refreshTool(); - } - } + protected virtual DragBox CreateDragBox(Action performSelect) => new DragBox(performSelect); protected override bool OnMouseDown(MouseDownEvent e) { @@ -129,22 +122,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { // Special case for when a drag happened instead of a click Schedule(() => endClickSelection()); - return e.Button == MouseButton.Left; - } - - protected override bool OnMouseMove(MouseMoveEvent e) - { - if (currentPlacement != null) - { - updatePlacementPosition(e.ScreenSpaceMousePosition); - return true; - } - - return base.OnMouseMove(e); } protected override bool OnDragStart(DragStartEvent e) @@ -154,36 +135,34 @@ namespace osu.Game.Screens.Edit.Compose.Components if (!beginSelectionMovement()) { - dragBox.UpdateDrag(e); - dragBox.FadeIn(250, Easing.OutQuint); + if (!DragBox.UpdateDrag(e)) + return false; + + DragBox.FadeIn(250, Easing.OutQuint); } return true; } - protected override bool OnDrag(DragEvent e) + protected override void OnDrag(DragEvent e) { if (e.Button == MouseButton.Right) - return false; + return; if (!moveCurrentSelection(e)) - dragBox.UpdateDrag(e); - - return true; + DragBox.UpdateDrag(e); } - protected override bool OnDragEnd(DragEndEvent e) + protected override void OnDragEnd(DragEndEvent e) { if (e.Button == MouseButton.Right) - return false; + return; if (!finishSelectionMovement()) { - dragBox.FadeOut(250, Easing.OutQuint); + DragBox.FadeOut(250, Easing.OutQuint); selectionHandler.UpdateVisibility(); } - - return true; } protected override bool OnKeyDown(KeyDownEvent e) @@ -201,8 +180,6 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - protected override bool OnKeyUp(KeyUpEvent e) => false; - public bool OnPressed(PlatformAction action) { switch (action.ActionType) @@ -215,35 +192,15 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - public bool OnReleased(PlatformAction action) => false; - - protected override void Update() + public void OnReleased(PlatformAction action) { - base.Update(); - - if (currentPlacement != null) - { - if (composer.CursorInPlacementArea) - currentPlacement.State = PlacementState.Shown; - else if (currentPlacement?.PlacementBegun == false) - currentPlacement.State = PlacementState.Hidden; - } } #region Blueprint Addition/Removal - private void addBlueprintFor(HitObject hitObject) - { - var drawable = composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject); - if (drawable == null) - return; - - addBlueprintFor(drawable); - } - private void removeBlueprintFor(HitObject hitObject) { - var blueprint = selectionBlueprints.Single(m => m.DrawableObject.HitObject == hitObject); + var blueprint = selectionBlueprints.SingleOrDefault(m => m.DrawableObject.HitObject == hitObject); if (blueprint == null) return; @@ -255,11 +212,9 @@ namespace osu.Game.Screens.Edit.Compose.Components selectionBlueprints.Remove(blueprint); } - private void addBlueprintFor(DrawableHitObject hitObject) + protected virtual void AddBlueprintFor(HitObject hitObject) { - refreshTool(); - - var blueprint = composer.CreateBlueprintFor(hitObject); + var blueprint = CreateBlueprintFor(hitObject); if (blueprint == null) return; @@ -271,37 +226,6 @@ namespace osu.Game.Screens.Edit.Compose.Components #endregion - #region Placement - - /// - /// Refreshes the current placement tool. - /// - private void refreshTool() - { - placementBlueprintContainer.Clear(); - currentPlacement = null; - - var blueprint = CurrentTool?.CreatePlacementBlueprint(); - - if (blueprint != null) - { - placementBlueprintContainer.Child = currentPlacement = blueprint; - - // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame - updatePlacementPosition(inputManager.CurrentState.Mouse.Position); - } - } - - private void updatePlacementPosition(Vector2 screenSpacePosition) - { - Vector2 snappedGridPosition = composer.GetSnappedPosition(ToLocalSpace(screenSpacePosition), 0).position; - Vector2 snappedScreenSpacePosition = ToScreenSpace(snappedGridPosition); - - currentPlacement.UpdatePosition(snappedScreenSpacePosition); - } - - #endregion - #region Selection /// @@ -437,7 +361,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // The final movement position, relative to screenSpaceMovementStartPosition Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; - (Vector2 snappedPosition, double snappedTime) = composer.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); + (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime); // Move the hitobjects if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition)))) @@ -474,7 +398,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (beatmap != null) { - beatmap.HitObjectAdded -= addBlueprintFor; + beatmap.HitObjectAdded -= AddBlueprintFor; beatmap.HitObjectRemoved -= removeBlueprintFor; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs new file mode 100644 index 0000000000..1576def38e --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -0,0 +1,149 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + /// + /// A blueprint container generally displayed as an overlay to a ruleset's playfield. + /// + public class ComposeBlueprintContainer : BlueprintContainer + { + [Resolved] + private HitObjectComposer composer { get; set; } + + private PlacementBlueprint currentPlacement; + + private readonly Container placementBlueprintContainer; + + private InputManager inputManager; + + private readonly IEnumerable drawableHitObjects; + + public ComposeBlueprintContainer(IEnumerable drawableHitObjects) + { + this.drawableHitObjects = drawableHitObjects; + + placementBlueprintContainer = new Container + { + RelativeSizeAxes = Axes.Both + }; + } + + [BackgroundDependencyLoader] + private void load() + { + AddInternal(placementBlueprintContainer); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + + #region Placement + + /// + /// Refreshes the current placement tool. + /// + private void refreshTool() + { + placementBlueprintContainer.Clear(); + currentPlacement = null; + + var blueprint = CurrentTool?.CreatePlacementBlueprint(); + + if (blueprint != null) + { + placementBlueprintContainer.Child = currentPlacement = blueprint; + + // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame + updatePlacementPosition(inputManager.CurrentState.Mouse.Position); + } + } + + private void updatePlacementPosition(Vector2 screenSpacePosition) + { + Vector2 snappedGridPosition = composer.GetSnappedPosition(ToLocalSpace(screenSpacePosition), 0).position; + Vector2 snappedScreenSpacePosition = ToScreenSpace(snappedGridPosition); + + currentPlacement.UpdatePosition(snappedScreenSpacePosition); + } + + #endregion + + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (currentPlacement != null) + { + updatePlacementPosition(e.ScreenSpaceMousePosition); + return true; + } + + return base.OnMouseMove(e); + } + + protected override void Update() + { + base.Update(); + + if (currentPlacement != null) + { + if (composer.CursorInPlacementArea) + currentPlacement.State = PlacementState.Shown; + else if (currentPlacement?.PlacementBegun == false) + currentPlacement.State = PlacementState.Hidden; + } + } + + protected sealed override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) + { + var drawable = drawableHitObjects.FirstOrDefault(d => d.HitObject == hitObject); + if (drawable == null) + return null; + + return CreateBlueprintFor(drawable); + } + + public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null; + + protected override void AddBlueprintFor(HitObject hitObject) + { + refreshTool(); + base.AddBlueprintFor(hitObject); + } + + private HitObjectCompositionTool currentTool; + + /// + /// The current placement tool. + /// + public HitObjectCompositionTool CurrentTool + { + get => currentTool; + set + { + if (currentTool == value) + return; + + currentTool = value; + + refreshTool(); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index 2a510e74fd..f522ca356f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -50,7 +50,12 @@ namespace osu.Game.Screens.Edit.Compose.Components }; } - public void UpdateDrag(MouseButtonEvent e) + /// + /// Handle a forwarded mouse event. + /// + /// The mouse event. + /// Whether the event should be handled and blocking. + public virtual bool UpdateDrag(MouseButtonEvent e) { var dragPosition = e.ScreenSpaceMousePosition; var dragStartPosition = e.ScreenSpaceMouseDownPosition; @@ -67,6 +72,7 @@ namespace osu.Game.Screens.Edit.Compose.Components box.Size = bottomRight - topLeft; performSelection?.Invoke(dragRectangle); + return true; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index e2d7855eb5..c00892573e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private Drawable outline; - [Resolved] + [Resolved(CanBeNull = true)] private IPlacementHandler placementHandler { get; set; } public SelectionHandler() @@ -87,7 +87,9 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete; + public void OnReleased(PlatformAction action) + { + } #endregion diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index b4f3b1f610..b8acc1ab44 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -143,10 +143,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return false; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { endUserDrag(); - return base.OnMouseUp(e); + base.OnMouseUp(e); } private void beginUserDrag() diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs index b20f2fa11d..f521d08ada 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs @@ -1,11 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -14,15 +17,19 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - internal class TimelineHitObjectDisplay : TimelinePart + internal class TimelineHitObjectDisplay : BlueprintContainer { private EditorBeatmap beatmap { get; } + private readonly TimelinePart content; + public TimelineHitObjectDisplay(EditorBeatmap beatmap) { RelativeSizeAxes = Axes.Both; this.beatmap = beatmap; + + AddInternal(content = new TimelinePart { RelativeSizeAxes = Axes.Both }); } [BackgroundDependencyLoader] @@ -40,17 +47,42 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }; } + protected override void LoadComplete() + { + base.LoadComplete(); + DragBox.Alpha = 0; + } + private void remove(HitObject h) { - foreach (var d in Children.OfType().Where(c => c.HitObject == h)) + foreach (var d in content.OfType().Where(c => c.HitObject == h)) d.Expire(); } private void add(HitObject h) { - var yOffset = Children.Count(d => d.X == h.StartTime); + var yOffset = content.Count(d => d.X == h.StartTime); - Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS }); + content.Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS }); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + base.OnMouseDown(e); + + return false; // tempoerary until we correctly handle selections. + } + + protected override DragBox CreateDragBox(Action performSelect) => new NoDragDragBox(performSelect); + + internal class NoDragDragBox : DragBox + { + public NoDragDragBox(Action performSelect) + : base(performSelect) + { + } + + public override bool UpdateDrag(MouseButtonEvent e) => false; } private class TimelineHitObjectRepresentation : CompositeDrawable 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..8d66cef16e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -26,6 +26,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Timing; @@ -34,7 +35,8 @@ using osu.Game.Users; namespace osu.Game.Screens.Edit { - public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler + [Cached(typeof(IBeatSnapProvider))] + public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IBeatSnapProvider { public override float BackgroundParallaxAmount => 0.1f; @@ -44,6 +46,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 +61,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 +70,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); @@ -77,11 +79,14 @@ namespace osu.Game.Screens.Edit clock.ChangeSource(sourceClock); playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); - editorBeatmap = new EditorBeatmap(playableBeatmap); + editorBeatmap = new EditorBeatmap(playableBeatmap, beatDivisor); dependencies.CacheAs(clock); dependencies.CacheAs(clock); + + // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); + dependencies.CacheAs(editorBeatmap); EditorMenuBar menuBar; @@ -90,7 +95,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 +211,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); @@ -243,7 +258,9 @@ namespace osu.Game.Screens.Edit return false; } - public bool OnReleased(GlobalAction action) => action == GlobalAction.Back; + public void OnReleased(GlobalAction action) + { + } public override void OnResuming(IScreen last) { @@ -292,8 +309,6 @@ namespace osu.Game.Screens.Edit } } - private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save()); - private void onModeChanged(ValueChangedEvent e) { currentScreen?.Exit(); @@ -329,5 +344,19 @@ 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); + } + + public double SnapTime(double referenceTime, double duration) => editorBeatmap.SnapTime(referenceTime, duration); + + public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime); + + public int BeatDivisor => beatDivisor.Value; } } diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 6ed74dfdb0..a67a0ff0f1 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -8,11 +8,12 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Edit { - public class EditorBeatmap : IBeatmap + public class EditorBeatmap : IBeatmap, IBeatSnapProvider { /// /// Invoked when a is added to this . @@ -31,11 +32,14 @@ namespace osu.Game.Screens.Edit public readonly IBeatmap PlayableBeatmap; + private readonly BindableBeatDivisor beatDivisor; + private readonly Dictionary> startTimeBindables = new Dictionary>(); - public EditorBeatmap(IBeatmap playableBeatmap) + public EditorBeatmap(IBeatmap playableBeatmap, BindableBeatDivisor beatDivisor = null) { PlayableBeatmap = playableBeatmap; + this.beatDivisor = beatDivisor; foreach (var obj in HitObjects) trackStartTime(obj); @@ -121,5 +125,17 @@ namespace osu.Game.Screens.Edit return list.Count - 1; } + + public double SnapTime(double referenceTime, double duration) + { + double beatLength = GetBeatLengthAtTime(referenceTime); + + // A 1ms offset prevents rounding errors due to minute variations in duration + return (int)((duration + 1) / beatLength) * beatLength; + } + + public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor; + + public int BeatDivisor => beatDivisor?.Value ?? 1; } } 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/Button.cs b/osu.Game/Screens/Menu/Button.cs index fac6b69e1f..6708ce0ba0 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -194,10 +194,10 @@ namespace osu.Game.Screens.Menu return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { boxHoverLayer.FadeTo(0, 1000, Easing.OutQuint); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnClick(ClickEvent e) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index ed8e4c70f9..d94f8aa11a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -211,7 +211,9 @@ namespace osu.Game.Screens.Menu } } - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } private bool goBack() { diff --git a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs index aaa3a77e74..db2faeb60a 100644 --- a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs +++ b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs @@ -24,16 +24,13 @@ namespace osu.Game.Screens.Menu return false; } - public bool OnReleased(GlobalAction action) + public void OnReleased(GlobalAction action) { if (action == GlobalAction.Back) { if (!Fired) AbortConfirm(); - return true; } - - return false; } } } 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..be2f29cbe9 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; @@ -353,12 +353,11 @@ namespace osu.Game.Screens.Menu return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { - if (e.Button != MouseButton.Left) return false; + if (e.Button != MouseButton.Left) return; logoBounceContainer.ScaleTo(1f, 500, Easing.OutElastic); - return true; } protected override bool OnClick(ClickEvent e) 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/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index adfbe977a4..98e2bc5a03 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -163,8 +163,6 @@ namespace osu.Game.Screens.Play // Don't let mouse down events through the overlay or people can click circles while paused. protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnMouseUp(MouseUpEvent e) => true; - protected override bool OnMouseMove(MouseMoveEvent e) => true; protected void AddButton(string text, Color4 colour, Action action) @@ -247,16 +245,8 @@ namespace osu.Game.Screens.Play return false; } - public bool OnReleased(GlobalAction action) + public void OnReleased(GlobalAction action) { - switch (action) - { - case GlobalAction.Back: - case GlobalAction.Select: - return true; - } - - return false; } private void buttonSelectionChanged(DialogButton button, bool isSelected) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 640224c057..684834123b 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; @@ -259,16 +259,14 @@ namespace osu.Game.Screens.Play.HUD return false; } - public bool OnReleased(GlobalAction action) + public void OnReleased(GlobalAction action) { switch (action) { case GlobalAction.Back: AbortConfirm(); - return true; + break; } - - return false; } protected override bool OnMouseDown(MouseDownEvent e) @@ -278,11 +276,10 @@ namespace osu.Game.Screens.Play.HUD return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { if (!e.HasAnyButtonPressed) AbortConfirm(); - return true; } } } diff --git a/osu.Game/Screens/Play/HotkeyExitOverlay.cs b/osu.Game/Screens/Play/HotkeyExitOverlay.cs index c18aecda55..8d7e2481bf 100644 --- a/osu.Game/Screens/Play/HotkeyExitOverlay.cs +++ b/osu.Game/Screens/Play/HotkeyExitOverlay.cs @@ -17,12 +17,11 @@ namespace osu.Game.Screens.Play return true; } - public bool OnReleased(GlobalAction action) + public void OnReleased(GlobalAction action) { - if (action != GlobalAction.QuickExit) return false; + if (action != GlobalAction.QuickExit) return; AbortConfirm(); - return true; } } } diff --git a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs index f1b851f2d5..58fd941f36 100644 --- a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs +++ b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs @@ -17,12 +17,11 @@ namespace osu.Game.Screens.Play return true; } - public bool OnReleased(GlobalAction action) + public void OnReleased(GlobalAction action) { - if (action != GlobalAction.QuickRetry) return false; + if (action != GlobalAction.QuickRetry) return; AbortConfirm(); - return true; } } } diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs index 33d675358c..00eddcc776 100644 --- a/osu.Game/Screens/Play/KeyCounterAction.cs +++ b/osu.Game/Screens/Play/KeyCounterAction.cs @@ -27,15 +27,14 @@ namespace osu.Game.Screens.Play return false; } - public bool OnReleased(T action, bool forwards) + public void OnReleased(T action, bool forwards) { if (!EqualityComparer.Default.Equals(action, Action)) - return false; + return; IsLit = false; if (!forwards) Decrement(); - return false; } } } diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs index 29188b6b59..187dcc1264 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs @@ -27,10 +27,10 @@ namespace osu.Game.Screens.Play return base.OnKeyDown(e); } - protected override bool OnKeyUp(KeyUpEvent e) + protected override void OnKeyUp(KeyUpEvent e) { if (e.Key == Key) IsLit = false; - return base.OnKeyUp(e); + base.OnKeyUp(e); } } } diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs index 828441de6e..e55525c5e8 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouse.cs @@ -45,10 +45,10 @@ namespace osu.Game.Screens.Play return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { if (e.Button == Button) IsLit = false; - return base.OnMouseUp(e); + base.OnMouseUp(e); } } } 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..3daf5b1ff1 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 @@ -143,7 +143,9 @@ namespace osu.Game.Screens.Play return false; } - public bool OnReleased(GlobalAction action) => false; + public void OnReleased(GlobalAction action) + { + } private class FadeContainer : Container, IStateful { @@ -202,10 +204,9 @@ namespace osu.Game.Screens.Play return true; } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { Show(); - return true; } public override void Hide() => State = Visibility.Hidden; @@ -311,10 +312,10 @@ namespace osu.Game.Screens.Play return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnClick(ClickEvent e) 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/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs index 38636b0c3b..d7eb5db125 100644 --- a/osu.Game/Screens/Ranking/ResultModeButton.cs +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -92,6 +92,6 @@ namespace osu.Game.Screens.Ranking protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint); - public string TooltipText { get; private set; } + public string TooltipText { get; } } } 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..b0d5be8140 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; @@ -363,7 +363,7 @@ namespace osu.Game.Screens.Select public class InfoLabel : Container, IHasTooltip { - public string TooltipText { get; private set; } + public string TooltipText { get; } public InfoLabel(BeatmapStatistic statistic) { 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/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index b77da36748..4dcab60548 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -118,10 +118,10 @@ namespace osu.Game.Screens.Select return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { box.FadeOut(Footer.TRANSITION_LENGTH, Easing.OutQuint); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnClick(ClickEvent e) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 14c9eb2035..9bc5181f6f 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -44,11 +44,11 @@ namespace osu.Game.Screens.Select return base.OnKeyDown(e); } - protected override bool OnKeyUp(KeyUpEvent e) + protected override void OnKeyUp(KeyUpEvent e) { secondaryActive = e.ShiftPressed; updateText(); - return base.OnKeyUp(e); + base.OnKeyUp(e); } private void updateText() 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/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index ff9beafb23..4e4653cb57 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -60,10 +60,10 @@ namespace osu.Game.Screens.Select.Options return base.OnMouseDown(e); } - protected override bool OnMouseUp(MouseUpEvent e) + protected override void OnMouseUp(MouseUpEvent e) { flash.FadeTo(0, 1000, Easing.OutQuint); - return base.OnMouseUp(e); + base.OnMouseUp(e); } protected override bool OnClick(ClickEvent e) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 8f7ad2022d..bf57def700 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -681,7 +681,9 @@ namespace osu.Game.Screens.Select return false; } - public bool OnReleased(GlobalAction action) => action == GlobalAction.Select; + public void OnReleased(GlobalAction action) + { + } protected override bool OnKeyDown(KeyDownEvent e) { diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 7a84ac009a..94d7395ecf 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -14,7 +14,7 @@ namespace osu.Game.Storyboards.Drawables { public class DrawableStoryboard : Container { - public Storyboard Storyboard { get; private set; } + public Storyboard Storyboard { get; } protected override Container Content { get; } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index a076bb54df..eabb78bac5 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -8,14 +8,14 @@ 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 { public class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable { - public StoryboardAnimation Animation { get; private set; } + public StoryboardAnimation Animation { get; } private bool flipH; diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs index fd2d441f34..39f5418902 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs @@ -10,7 +10,7 @@ namespace osu.Game.Storyboards.Drawables { public class DrawableStoryboardLayer : LifetimeManagementContainer { - public StoryboardLayer Layer { get; private set; } + public StoryboardLayer Layer { get; } public bool Enabled; public override bool IsPresent => Enabled && base.IsPresent; diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index ac795b3349..d8d3248659 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -8,14 +8,14 @@ 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 { public class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable { - public StoryboardSprite Sprite { get; private set; } + public StoryboardSprite Sprite { get; } private bool flipH; diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 22e1929419..f411ad04f3 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -16,7 +16,7 @@ namespace osu.Game.Storyboards private readonly List loops = new List(); private readonly List triggers = new List(); - public string Path { get; set; } + public string Path { get; } public bool IsDrawable => HasCommands; public Anchor Origin; 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..9b431e6425 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..4dc7403553 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 diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 12571be31d..15ea20084d 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -19,8 +19,8 @@ HINT DO_NOT_SHOW HINT - HINT - HINT + WARNING + WARNING WARNING WARNING WARNING @@ -765,6 +765,7 @@ See the LICENCE file in the repository root for full licence text. <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True True True